├── .editorconfig ├── .eslintrc.js ├── .flowconfig ├── .github ├── CODEOWNERS └── workflows │ ├── main.yml │ └── publish.yml ├── .gitignore ├── .husky ├── commit-msg └── pre-commit ├── .npmrc ├── .prettierignore ├── .prettierrc.json ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── SECURITY.md ├── babel.config.json ├── commitlint.config.js ├── demo ├── README.md ├── iframe-password.html ├── iframe-validation-fields.html ├── index.html └── package.json ├── dist ├── cross-domain-utils.js ├── cross-domain-utils.min.js ├── cross-domain-utils.min.js.map └── module │ ├── constants.js │ ├── index.flow.js │ ├── index.js │ ├── types.js │ ├── util.js │ └── utils.js ├── karma.conf.js ├── package.json ├── src ├── constants.js ├── index.flow.js ├── index.js ├── types.js ├── util.js └── utils.js ├── test ├── index.js ├── tests │ ├── closeWindow.js │ ├── getActualDomain.js │ ├── getAllFramesInWindow.js │ ├── getDomain.js │ ├── getDomainFromUrl.js │ ├── getOpener.js │ ├── getParent.js │ ├── getParents.js │ ├── getUserAgent.js │ ├── index.js │ ├── isBlankDomain.js │ ├── isFileProtocol.js │ ├── isSameDomain.js │ ├── matchDomain.js │ └── stringifyDomainPattern.js ├── util.js └── win.js └── webpack.config.js /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: http://EditorConfig.org 2 | 3 | # Howto with your editor: http://editorconfig.org/#download 4 | # Sublime: https://github.com/sindresorhus/editorconfig-sublime 5 | 6 | # top-most EditorConfig file 7 | root = true 8 | 9 | # Unix-style newlines with a newline ending every file 10 | [**] 11 | end_of_line = lf 12 | insert_final_newline = true 13 | 14 | # Standard at: https://github.com/felixge/node-style-guide 15 | [**.js, **.json] 16 | trim_trailing_whitespace = true 17 | indent_style = space 18 | indent_size = 4 19 | quote_type = single 20 | curly_bracket_next_line = false 21 | spaces_around_operators = true 22 | space_after_control_statements = true 23 | space_after_anonymous_functions = true 24 | spaces_in_brackets = false 25 | 26 | # No Standard. Please document a standard if different from .js 27 | [**.yml, **.css] 28 | trim_trailing_whitespace = true 29 | indent_style = tab 30 | 31 | [**.html] 32 | trim_trailing_whitespace = true 33 | indent_style = space 34 | indent_size = 4 35 | 36 | # No standard. Please document a standard if different from .js 37 | [**.md] 38 | indent_style = tab 39 | 40 | # Standard at: 41 | [Makefile] 42 | indent_style = tab 43 | 44 | # The indentation in package.json will always need to be 2 spaces 45 | # https://github.com/npm/npm/issues/4718 46 | [package.json, bower.json] 47 | indent_style = space 48 | indent_size = 2 49 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | module.exports = { 4 | extends: "./node_modules/@krakenjs/eslint-config-grumbler", 5 | 6 | globals: { 7 | __TEST__: true, 8 | }, 9 | }; 10 | -------------------------------------------------------------------------------- /.flowconfig: -------------------------------------------------------------------------------- 1 | [ignore] 2 | .*/node_modules/gulp-flowtype 3 | .*/node_modules/babel-plugin-flow-runtime 4 | .*/node_modules/flow-runtime 5 | .*/node_modules/npm 6 | .*/node_modules/resolve 7 | .*/dist/module 8 | [include] 9 | [libs] 10 | flow-typed 11 | ./src/index.flow.js 12 | [options] 13 | module.name_mapper='^src\(.*\)$' -> '/src/\1' 14 | experimental.const_params=false 15 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # Owner for everything in the repo 2 | * @krakenjs/checkout-sdk 3 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | on: 3 | push: 4 | branches: 5 | - main 6 | pull_request: {} 7 | jobs: 8 | main: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: ⬇️ Checkout repo 12 | uses: actions/checkout@v2 13 | with: 14 | fetch-depth: 0 15 | 16 | - name: ⎔ Setup node 17 | uses: actions/setup-node@v2 18 | with: 19 | node-version: "14" 20 | registry-url: "https://registry.npmjs.org" 21 | 22 | - name: 📥 Download deps 23 | uses: bahmutov/npm-install@v1 24 | with: 25 | useLockFile: false 26 | 27 | - name: 👕 Lint commit messages 28 | uses: wagoid/commitlint-github-action@v4 29 | 30 | - name: ▶️ Run flow-typed script 31 | run: npm run flow-typed 32 | 33 | - name: ▶️ Run build script 34 | run: npm run build 35 | 36 | - name: ⬆️ Upload karma coverage report 37 | uses: codecov/codecov-action@v2 38 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: publish to npm 2 | on: workflow_dispatch 3 | jobs: 4 | main: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - name: ⬇️ Checkout repo 8 | uses: actions/checkout@v2 9 | with: 10 | fetch-depth: 0 11 | 12 | - name: ⎔ Setup node 13 | # sets up the .npmrc file to publish to npm 14 | uses: actions/setup-node@v2 15 | with: 16 | node-version: "14" 17 | registry-url: "https://registry.npmjs.org" 18 | 19 | - name: 📥 Download deps 20 | uses: bahmutov/npm-install@v1 21 | with: 22 | useLockFile: false 23 | 24 | - name: Configure git user 25 | run: | 26 | git config --global user.email ${{ github.actor }}@users.noreply.github.com 27 | git config --global user.name ${{ github.actor }} 28 | 29 | - name: Publish to npm 30 | run: npm run release 31 | env: 32 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 33 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | .DS_Store 6 | 7 | # Runtime data 8 | pids 9 | *.pid 10 | *.seed 11 | *.pid.lock 12 | 13 | # Directory for instrumented libs generated by jscoverage/JSCover 14 | lib-cov 15 | 16 | # Coverage directory used by tools like istanbul 17 | coverage 18 | 19 | # nyc test coverage 20 | .nyc_output 21 | 22 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 23 | .grunt 24 | 25 | # node-waf configuration 26 | .lock-wscript 27 | 28 | # Compiled binary addons (http://nodejs.org/api/addons.html) 29 | build/Release 30 | 31 | # Dependency directories 32 | node_modules 33 | jspm_packages 34 | 35 | # Optional npm cache directory 36 | .npm 37 | 38 | # Optional eslint cache 39 | .eslintcache 40 | 41 | # Optional REPL history 42 | .node_repl_history 43 | 44 | # Output of 'npm pack' 45 | *.tgz 46 | 47 | # Yarn Integrity file 48 | .yarn-integrity 49 | 50 | # Flow Typed 51 | flow-typed 52 | 53 | .idea 54 | package-lock.json 55 | -------------------------------------------------------------------------------- /.husky/commit-msg: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | npx --no -- commitlint --edit "$1" 5 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | npx lint-staged 5 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | registry=https://registry.npmjs.org 2 | package-lock=false 3 | save=false 4 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | build 2 | dist 3 | coverage 4 | flow-typed 5 | CHANGELOG.md 6 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. 4 | 5 | ## [3.1.0](https://github.com/krakenjs/cross-domain-utils/compare/v3.0.2...v3.1.0) (2023-06-12) 6 | 7 | 8 | ### Features 9 | 10 | * grumbler-scripts@8 ([#38](https://github.com/krakenjs/cross-domain-utils/issues/38)) ([5197ff3](https://github.com/krakenjs/cross-domain-utils/commit/5197ff33a61b1b5c766d284c61716098c384d268)) 11 | 12 | 13 | ### Bug Fixes 14 | 15 | * **type:** domain matcher ([5b6724d](https://github.com/krakenjs/cross-domain-utils/commit/5b6724dec581d3c87974147ffc93fa3149b5ee83)) 16 | 17 | 18 | * add badges to readme ([#27](https://github.com/krakenjs/cross-domain-utils/issues/27)) ([c61aa24](https://github.com/krakenjs/cross-domain-utils/commit/c61aa24cc8bfaeda651abea9a1403407319c6e65)) 19 | * add coverage with codecov ([#31](https://github.com/krakenjs/cross-domain-utils/issues/31)) ([a71904e](https://github.com/krakenjs/cross-domain-utils/commit/a71904e3843af19687195b0da1ef2b2c3a8f7464)) 20 | * commit dist folder when releasing ([#34](https://github.com/krakenjs/cross-domain-utils/issues/34)) ([7e7bf54](https://github.com/krakenjs/cross-domain-utils/commit/7e7bf545bab4791ad28411af414d51de836cb87f)) 21 | * **deps:** use grumbler-scripts v5 ([#29](https://github.com/krakenjs/cross-domain-utils/issues/29)) ([c1038b8](https://github.com/krakenjs/cross-domain-utils/commit/c1038b8b2a5b8ffbd9621f4fbbf747b45b8eeef3)) 22 | * **docs:** update github actions badge url ([6b8d5a7](https://github.com/krakenjs/cross-domain-utils/commit/6b8d5a796f66f31876620236c74d1e51aa1fbd87)) 23 | * fix build badge ([d66a149](https://github.com/krakenjs/cross-domain-utils/commit/d66a149f21409e603dacbfd85ac1eb70f3f99367)) 24 | * move devDependencies to [@krakenjs](https://github.com/krakenjs) scope ([a5e9c68](https://github.com/krakenjs/cross-domain-utils/commit/a5e9c68825925d675bccd6eed03aba20399bb028)) 25 | * move devDependencies to [@krakenjs](https://github.com/krakenjs) scope ([f00a02d](https://github.com/krakenjs/cross-domain-utils/commit/f00a02d492660a7acade12f3af6fbdc98797f1ab)) 26 | * remove quotes from publish ([58c55aa](https://github.com/krakenjs/cross-domain-utils/commit/58c55aae7e1985e76c9d53e7f8ccc6cb7ce88154)) 27 | * remove token from publish action ([3c51659](https://github.com/krakenjs/cross-domain-utils/commit/3c5165947924d96e6628cdd30379ffd2e84dd0d8)) 28 | * update changelog generation to include all types ([97c0ab6](https://github.com/krakenjs/cross-domain-utils/commit/97c0ab68442791bfa3147fd6002312e887a42686)) 29 | * update package description ([1ccd651](https://github.com/krakenjs/cross-domain-utils/commit/1ccd651c8d20c64876aabe22ebeef2a7f5aea18d)) 30 | * use 'npm prepare' for husky ([#32](https://github.com/krakenjs/cross-domain-utils/issues/32)) ([d5d9cbe](https://github.com/krakenjs/cross-domain-utils/commit/d5d9cbe920c3d7948ac2dc9ee29b5ed7ce0d50c8)) 31 | * use prettier for code formatting ([77561d8](https://github.com/krakenjs/cross-domain-utils/commit/77561d8da9876bd8a8169bdcd0f37c4bb8468e9a)) 32 | 33 | ### [3.0.2](https://github.com/krakenjs/cross-domain-utils/compare/v3.0.1...v3.0.2) (2022-02-16) 34 | 35 | ### [3.0.1](https://github.com/krakenjs/cross-domain-utils/compare/v3.0.0...v3.0.1) (2022-02-16) 36 | 37 | ## [3.0.0](https://github.com/krakenjs/cross-domain-utils/compare/v2.0.38...v3.0.0) (2022-02-15) 38 | 39 | 40 | ### ⚠ BREAKING CHANGES 41 | 42 | * move to @krakenjs npm scope 43 | 44 | * move to [@krakenjs](https://github.com/krakenjs) npm scope ([516cd2e](https://github.com/krakenjs/cross-domain-utils/commit/516cd2e88ed58416528ddb4d922515ea350cfe22)) 45 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to cross-domain-utils 2 | 3 | We are always looking for ways to make our modules better. Adding features and fixing bugs allows everyone who depends 4 | on this code to create better, more stable applications. 5 | Feel free to raise a pull request to us. Our team would review your proposed modifications and, if appropriate, merge 6 | your changes into our code. Ideas and other comments are also welcome. 7 | 8 | ## Getting Started 9 | 10 | 1. Create your own [fork](https://help.github.com/articles/fork-a-repo) of this [repository](../../fork). 11 | 12 | ```bash 13 | # Clone it 14 | $ git clone git@github.com:krakenjs/cross-domain-utils.git 15 | 16 | # Change directory 17 | $ cd cross-domain-utils 18 | 19 | # Add the upstream repo 20 | $ git remote add upstream git://github.com/krakenjs/cross-domain-utils.git 21 | 22 | # Get the latest upstream changes 23 | $ git pull upstream 24 | 25 | # Install dependencies 26 | $ npm install 27 | 28 | # Run scripts to verify installation 29 | $ npm test 30 | $ npm run-script lint 31 | $ npm run-script cover 32 | ``` 33 | 34 | ## Making Changes 35 | 36 | 1. Make sure that your changes adhere to the current coding conventions used throughout the project, indentation, accurate comments, etc. 37 | 2. Lint your code regularly and ensure it passes prior to submitting a PR: 38 | `$ npm run lint`. 39 | 3. Ensure existing tests pass (`$ npm test`) and include test cases which fail without your change and succeed with it. 40 | 41 | ## Submitting Changes 42 | 43 | 1. Ensure that no errors are generated by ESLint. 44 | 2. Commit your changes in logical chunks, i.e. keep your changes small per single commit. 45 | 3. Locally merge (or rebase) the upstream branch into your topic branch: `$ git pull upstream && git merge`. 46 | 4. Push your topic branch up to your fork: `$ git push origin `. 47 | 5. Open a [Pull Request](https://help.github.com/articles/using-pull-requests) with a clear title and description. 48 | 49 | If you have any questions about contributing, please feel free to contact us by posting your questions on GitHub. 50 | 51 | Copyright 2017, PayPal under [the Apache 2.0 license](LICENSE). 52 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | https://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | Copyright 2017 PayPal 179 | 180 | Licensed under the Apache License, Version 2.0 (the "License"); 181 | you may not use this file except in compliance with the License. 182 | You may obtain a copy of the License at 183 | 184 | https://www.apache.org/licenses/LICENSE-2.0 185 | 186 | Unless required by applicable law or agreed to in writing, software 187 | distributed under the License is distributed on an "AS IS" BASIS, 188 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 189 | See the License for the specific language governing permissions and 190 | limitations under the License. 191 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Cross Domain Utils 2 | 3 | [![build status][build-badge]][build] 4 | [![code coverage][coverage-badge]][coverage] 5 | [![npm version][version-badge]][package] 6 | [![apache license][license-badge]][license] 7 | 8 | [build-badge]: https://img.shields.io/github/actions/workflow/status/krakenjs/cross-domain-utils/main.yml?branch=main&logo=github&style=flat-square 9 | [build]: https://github.com/krakenjs/cross-domain-utils/actions/workflows/main.yml?query=workflow:build 10 | [coverage-badge]: https://img.shields.io/codecov/c/github/krakenjs/cross-domain-utils.svg?style=flat-square 11 | [coverage]: https://codecov.io/github/krakenjs/cross-domain-utils 12 | [version-badge]: https://img.shields.io/npm/v/@krakenjs/cross-domain-utils.svg?style=flat-square 13 | [package]: https://www.npmjs.com/package/@krakenjs/cross-domain-utils 14 | [license-badge]: https://img.shields.io/npm/l/@krakenjs/cross-domain-utils.svg?style=flat-square 15 | [license]: https://github.com/krakenjs/cross-domain-utils/blob/main/LICENSE 16 | 17 | A set of utilities for dealing with cross-domain windows 18 | 19 | ## Installation 20 | 21 | As of version 3 this package will be published under the `@krakenjs` scope. 22 | 23 | ``` 24 | npm install @krakenjs/cross-domain-utils 25 | ``` 26 | 27 | ## Public methods 28 | 29 | ### `getDomain(win : Window) => string` 30 | 31 | Get the full domain of the specified window, as a string. 32 | 33 | - `win` must be a window on the same domain as the current window, or an exception will be raised 34 | - This can be overridden / mocked by setting `win.mockDomain = 'mock://some-domain.com';`. `mock://` is required to ensure the window can not spoof actual `http://` or `https://` domains 35 | 36 | ### `getDomainFromUrl(url : string) => string` 37 | 38 | Get the full domain from the specified url, as a string. 39 | 40 | - it will try to extract the domain from the url string if it starts with well known protocols (`http://`, `https://`, `file://`, and additionally `mock://` urls) 41 | - if url string does not contain a known protocol, it will try to extract the domain calling `getDomain` using the current window as input 42 | 43 | ### `getActualDomain(win : Window) => string` 44 | 45 | Same as `getDomain` but overriding / mocking is disabled. it will return the real full domain of the specified window. 46 | 47 | ### `isBlankDomain(win : Window) => boolean` 48 | 49 | Returns if the domain for the specified window is blank, or `about:blank` 50 | 51 | - `win` must be a window on the same domain as the current window, or an exception will be raised 52 | - `win` may be a window or iframe that has been newly opened by the current window 53 | 54 | ### `isSameDomain(win : Window) => boolean` 55 | 56 | Returns if the specified window is on the same domain as the current window. 57 | 58 | - Does so without raising any errors or console warnings, even in Safari where wrapping the check `try/catch` still raises a console warning. 59 | 60 | ### `getParent(win : Window) => ?Window` 61 | 62 | Gets the parent of the specified window, if the window has a parent. 63 | 64 | - Only returns the parent of iframes 65 | - Returns void if the window is the top-level window 66 | 67 | ### `getOpener(win : Window) => ?Window` 68 | 69 | Gets the opener of the specified window, if the window has an opener. 70 | 71 | - Only returns the opener of windows opened with `window.open` 72 | - Returns void if the window is the top-level window 73 | 74 | ### `getParents(win : Window) => Array` 75 | 76 | Gets all of the hierarchical parents of the specified window. 77 | 78 | - Only returns the parents of iframes 79 | - Returns a blank array if the window is the top-level window 80 | 81 | ### `isAncestorParent(ancestor : Window, win : Window) => boolean` 82 | 83 | Returns true if the `ancestor` is a direct or non-direct parent of the specified window. 84 | 85 | ### `getFrames(win : Window) => Array` 86 | 87 | Returns an array of all direct child frames found in a given window. 88 | 89 | - Only returns direct children 90 | 91 | ### `getAllChildFrames(win : Window) => Array` 92 | 93 | Returns an array of all recursive child frames found in a given window, and in the child-frames of that window. 94 | 95 | - Recursively searches for all direct and indirect children 96 | 97 | ### `getTop(win : Window) => Window` 98 | 99 | Gets the top-level parent of the specified window. 100 | 101 | ### `getAllFramesInWindow(win : Window) => Array` 102 | 103 | Returns an array of all recursive child frames found in a given window, and in the child-frames of that window, including the specified window. 104 | 105 | - Recursively searches for all direct and indirect children 106 | 107 | ### `isTop(win : Window) => boolean` 108 | 109 | Returns true if the specified window is the top level window, without any parents. 110 | 111 | ### `isFrameWindowClosed(frame : HTMLIFrameElement) => boolean` 112 | 113 | Returns true if the window attached to an iframe element is closed, by checking if the frame is still attached to an open document. 114 | 115 | - Prefer `isWindowClosed` when possible 116 | 117 | ### `isWindowClosed(win : Window) => boolean` 118 | 119 | Returns true if a window has been closed 120 | 121 | - In IE/Edge, this check is not 100% reliable for frame windows where the frame has been removed from the DOM. Such window objects give no indication that they are closed. 122 | 123 | ### `getUserAgent(win : Window) => string` 124 | 125 | Gets the user agent for the specified window 126 | 127 | - Window must be on the same domain as the current window 128 | - Uses `win.navigator.mockUserAgent` if specified, to allow for mocking / tests. 129 | 130 | ### `getFrameByName(win : Window, name : string) => ?Window` 131 | 132 | Gets a frame window with the given name, if it exists as a child of the specified window. 133 | 134 | ### `findChildFrameByName(win : Window, name : string) => ?Window` 135 | 136 | Recursively searches for a given frame window inside the children specified window. 137 | 138 | ### `findFrameByName(win : Window, name : string) => ?Window` 139 | 140 | Recursively searches for a given frame window inside the entire frame hierarchy of the specified window. 141 | 142 | - Searches both the children and the parent windows recursively for the frame. 143 | 144 | ### `isParent(parent : Window, child : Window) => boolean` 145 | 146 | Returns true if the specified parent window is the parent of the specified child window. 147 | 148 | ### `isOpener(opener : Window, child : Window) => boolean` 149 | 150 | Returns true if the specified opener window is the opener of the specified child window. 151 | 152 | ### `getAncestor(win : Window) => ?Window` 153 | 154 | Gets either the parent or the opener of the specified window, if either is present. 155 | 156 | ### `getAncestors(win : Window) => Array` 157 | 158 | Recursively gets either the parent or the opener of the specified window, if either is present, and returns an array of the entire ancestor hierarchy. 159 | 160 | ### `isAncestor(ancestor : Window, child : Window) => boolean` 161 | 162 | Returns true if the specified ancestor window is the parent or the opener of the specified child window. 163 | 164 | ### `isPopup(win : Window) => boolean` 165 | 166 | Returns true if the specified window has been opened with `window.open` (i.e. if it is a popup window) 167 | 168 | ### `isIframe(win : Window) => boolean` 169 | 170 | Returns true if the specified window has been opened as an iframe. 171 | 172 | ### `getDistanceFromTop(win : Window) => number` 173 | 174 | Gets the numerical distance from the specified window to the top level window in that window's hierarchy. 175 | 176 | - If the specified window is at the top, this will return 0. 177 | 178 | ### `getNthParent(win : Window, n : number) => ?Window` 179 | 180 | Gets the window `n` levels up from the specified window, if it exists. 181 | 182 | ### `isSameTopWindow(win1 : window, win2 : Window) => boolean` 183 | 184 | Returns true if the windows are in the same hierarchy, with the same top level window 185 | 186 | - Will return false if one of the windows is a popup and the other window is not a frame inside that popup. 187 | 188 | ### `isWindow(obj : Window) => boolean` 189 | 190 | Returns true if the specified object is a window instance 191 | 192 | ### `onCloseWindow(win : Window, callback : Function, interval : number) => { cancel : Function }` 193 | 194 | Calls the callback when the specified window closes, with checks running on the specified interval. 195 | 196 | - Returns a listener object with a `.cancel()` method, to stop the loop 197 | 198 | ### `matchDomain(pattern : (string | RegExp | Array), domain : string) => boolean` 199 | 200 | Returns true if the specified domain matches the pattern. The pattern can be one of: 201 | 202 | - A literal string 203 | - A regular expression 204 | - An array of possible domains as strings 205 | 206 | # Tasks 207 | 208 | All of the tasks are listed in the package.json file under the scripts section 209 | 210 | | Command | Description | 211 | | ------------- | :--------------------------------------: | 212 | | npm run build | Builds the dist files | 213 | | npm test | Runs the test suite. Lint + Type + Karma | 214 | 215 | # Debugging 216 | 217 | Run the debug task and pass the next tasks as argument. 218 | 219 | ``` 220 | npm run debug -- npm run build 221 | npm run debug -- npm test 222 | npm run debug -- npm run karma -- --browsers=Chrome 223 | ``` 224 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | We take security very seriously and ask that you follow the following process. 4 | 5 | ## Contact us 6 | 7 | If you think you may have found a security bug we ask that you privately send the details to DL-PP-Kraken-Js@ebay.com. Please make sure to use a descriptive title in the email. 8 | 9 | ## Expectations 10 | 11 | We will generally get back to you within **24 hours**, but a more detailed response may take up to **48 hours**. If you feel we're not responding back in time, please send us a message _without detail_ on Twitter [@kraken_js](https://twitter.com/kraken_js). 12 | 13 | ## History 14 | 15 | No reported issues 16 | -------------------------------------------------------------------------------- /babel.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@krakenjs/babel-config-grumbler/babelrc-browser" 3 | } 4 | -------------------------------------------------------------------------------- /commitlint.config.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | module.exports = { 3 | extends: ["@commitlint/config-conventional"], 4 | }; 5 | -------------------------------------------------------------------------------- /demo/README.md: -------------------------------------------------------------------------------- 1 | #### Demo 2 | 3 | `npm start` 4 | 5 | http://localhost:8080/ 6 | -------------------------------------------------------------------------------- /demo/iframe-password.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Password Field 6 | 7 | 8 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 | 111 | 112 | 113 | -------------------------------------------------------------------------------- /demo/iframe-validation-fields.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Validation Errors 6 | 7 | 8 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /demo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Top Level Window 6 | 7 | 8 | 9 | 15 | 16 | 17 | 18 |
19 |
20 |
21 |

22 | 23 | 24 | 32 | 33 | 34 | 48 | 49 | 56 |
57 |
58 |
59 | 60 | 61 | 62 | 106 | 107 | 108 | -------------------------------------------------------------------------------- /demo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "start": "npx http-server" 4 | }, 5 | "licenses": [ 6 | { 7 | "type": "Apache 2.0", 8 | "url": "http://www.apache.org/licenses/LICENSE-2.0.html" 9 | } 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /dist/cross-domain-utils.js: -------------------------------------------------------------------------------- 1 | !function(root, factory) { 2 | "object" == typeof exports && "object" == typeof module ? module.exports = factory() : "function" == typeof define && define.amd ? define("crossDomainUtils", [], factory) : "object" == typeof exports ? exports.crossDomainUtils = factory() : root.crossDomainUtils = factory(); 3 | }("undefined" != typeof self ? self : this, (function() { 4 | return function(modules) { 5 | var installedModules = {}; 6 | function __webpack_require__(moduleId) { 7 | if (installedModules[moduleId]) return installedModules[moduleId].exports; 8 | var module = installedModules[moduleId] = { 9 | i: moduleId, 10 | l: !1, 11 | exports: {} 12 | }; 13 | modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); 14 | module.l = !0; 15 | return module.exports; 16 | } 17 | __webpack_require__.m = modules; 18 | __webpack_require__.c = installedModules; 19 | __webpack_require__.d = function(exports, name, getter) { 20 | __webpack_require__.o(exports, name) || Object.defineProperty(exports, name, { 21 | enumerable: !0, 22 | get: getter 23 | }); 24 | }; 25 | __webpack_require__.r = function(exports) { 26 | "undefined" != typeof Symbol && Symbol.toStringTag && Object.defineProperty(exports, Symbol.toStringTag, { 27 | value: "Module" 28 | }); 29 | Object.defineProperty(exports, "__esModule", { 30 | value: !0 31 | }); 32 | }; 33 | __webpack_require__.t = function(value, mode) { 34 | 1 & mode && (value = __webpack_require__(value)); 35 | if (8 & mode) return value; 36 | if (4 & mode && "object" == typeof value && value && value.__esModule) return value; 37 | var ns = Object.create(null); 38 | __webpack_require__.r(ns); 39 | Object.defineProperty(ns, "default", { 40 | enumerable: !0, 41 | value: value 42 | }); 43 | if (2 & mode && "string" != typeof value) for (var key in value) __webpack_require__.d(ns, key, function(key) { 44 | return value[key]; 45 | }.bind(null, key)); 46 | return ns; 47 | }; 48 | __webpack_require__.n = function(module) { 49 | var getter = module && module.__esModule ? function() { 50 | return module.default; 51 | } : function() { 52 | return module; 53 | }; 54 | __webpack_require__.d(getter, "a", getter); 55 | return getter; 56 | }; 57 | __webpack_require__.o = function(object, property) { 58 | return {}.hasOwnProperty.call(object, property); 59 | }; 60 | __webpack_require__.p = ""; 61 | return __webpack_require__(__webpack_require__.s = 0); 62 | }([ function(module, __webpack_exports__, __webpack_require__) { 63 | "use strict"; 64 | __webpack_require__.r(__webpack_exports__); 65 | __webpack_require__.d(__webpack_exports__, "getActualProtocol", (function() { 66 | return getActualProtocol; 67 | })); 68 | __webpack_require__.d(__webpack_exports__, "getProtocol", (function() { 69 | return getProtocol; 70 | })); 71 | __webpack_require__.d(__webpack_exports__, "isFileProtocol", (function() { 72 | return isFileProtocol; 73 | })); 74 | __webpack_require__.d(__webpack_exports__, "isAboutProtocol", (function() { 75 | return isAboutProtocol; 76 | })); 77 | __webpack_require__.d(__webpack_exports__, "isMockProtocol", (function() { 78 | return isMockProtocol; 79 | })); 80 | __webpack_require__.d(__webpack_exports__, "getParent", (function() { 81 | return getParent; 82 | })); 83 | __webpack_require__.d(__webpack_exports__, "getOpener", (function() { 84 | return getOpener; 85 | })); 86 | __webpack_require__.d(__webpack_exports__, "canReadFromWindow", (function() { 87 | return canReadFromWindow; 88 | })); 89 | __webpack_require__.d(__webpack_exports__, "getActualDomain", (function() { 90 | return getActualDomain; 91 | })); 92 | __webpack_require__.d(__webpack_exports__, "getDomain", (function() { 93 | return getDomain; 94 | })); 95 | __webpack_require__.d(__webpack_exports__, "isBlankDomain", (function() { 96 | return isBlankDomain; 97 | })); 98 | __webpack_require__.d(__webpack_exports__, "isActuallySameDomain", (function() { 99 | return isActuallySameDomain; 100 | })); 101 | __webpack_require__.d(__webpack_exports__, "isSameDomain", (function() { 102 | return isSameDomain; 103 | })); 104 | __webpack_require__.d(__webpack_exports__, "assertSameDomain", (function() { 105 | return assertSameDomain; 106 | })); 107 | __webpack_require__.d(__webpack_exports__, "getParents", (function() { 108 | return getParents; 109 | })); 110 | __webpack_require__.d(__webpack_exports__, "isAncestorParent", (function() { 111 | return isAncestorParent; 112 | })); 113 | __webpack_require__.d(__webpack_exports__, "getFrames", (function() { 114 | return getFrames; 115 | })); 116 | __webpack_require__.d(__webpack_exports__, "getAllChildFrames", (function() { 117 | return getAllChildFrames; 118 | })); 119 | __webpack_require__.d(__webpack_exports__, "getTop", (function() { 120 | return getTop; 121 | })); 122 | __webpack_require__.d(__webpack_exports__, "getNextOpener", (function() { 123 | return getNextOpener; 124 | })); 125 | __webpack_require__.d(__webpack_exports__, "getUltimateTop", (function() { 126 | return getUltimateTop; 127 | })); 128 | __webpack_require__.d(__webpack_exports__, "getAllFramesInWindow", (function() { 129 | return getAllFramesInWindow; 130 | })); 131 | __webpack_require__.d(__webpack_exports__, "getAllWindows", (function() { 132 | return getAllWindows; 133 | })); 134 | __webpack_require__.d(__webpack_exports__, "isTop", (function() { 135 | return isTop; 136 | })); 137 | __webpack_require__.d(__webpack_exports__, "isFrameWindowClosed", (function() { 138 | return isFrameWindowClosed; 139 | })); 140 | __webpack_require__.d(__webpack_exports__, "isWindowClosed", (function() { 141 | return isWindowClosed; 142 | })); 143 | __webpack_require__.d(__webpack_exports__, "linkFrameWindow", (function() { 144 | return linkFrameWindow; 145 | })); 146 | __webpack_require__.d(__webpack_exports__, "getUserAgent", (function() { 147 | return getUserAgent; 148 | })); 149 | __webpack_require__.d(__webpack_exports__, "getFrameByName", (function() { 150 | return getFrameByName; 151 | })); 152 | __webpack_require__.d(__webpack_exports__, "findChildFrameByName", (function() { 153 | return findChildFrameByName; 154 | })); 155 | __webpack_require__.d(__webpack_exports__, "findFrameByName", (function() { 156 | return findFrameByName; 157 | })); 158 | __webpack_require__.d(__webpack_exports__, "isParent", (function() { 159 | return isParent; 160 | })); 161 | __webpack_require__.d(__webpack_exports__, "isOpener", (function() { 162 | return isOpener; 163 | })); 164 | __webpack_require__.d(__webpack_exports__, "getAncestor", (function() { 165 | return getAncestor; 166 | })); 167 | __webpack_require__.d(__webpack_exports__, "getAncestors", (function() { 168 | return getAncestors; 169 | })); 170 | __webpack_require__.d(__webpack_exports__, "isAncestor", (function() { 171 | return isAncestor; 172 | })); 173 | __webpack_require__.d(__webpack_exports__, "isPopup", (function() { 174 | return isPopup; 175 | })); 176 | __webpack_require__.d(__webpack_exports__, "isIframe", (function() { 177 | return isIframe; 178 | })); 179 | __webpack_require__.d(__webpack_exports__, "isFullpage", (function() { 180 | return isFullpage; 181 | })); 182 | __webpack_require__.d(__webpack_exports__, "getDistanceFromTop", (function() { 183 | return getDistanceFromTop; 184 | })); 185 | __webpack_require__.d(__webpack_exports__, "getNthParent", (function() { 186 | return getNthParent; 187 | })); 188 | __webpack_require__.d(__webpack_exports__, "getNthParentFromTop", (function() { 189 | return getNthParentFromTop; 190 | })); 191 | __webpack_require__.d(__webpack_exports__, "isSameTopWindow", (function() { 192 | return isSameTopWindow; 193 | })); 194 | __webpack_require__.d(__webpack_exports__, "matchDomain", (function() { 195 | return matchDomain; 196 | })); 197 | __webpack_require__.d(__webpack_exports__, "stringifyDomainPattern", (function() { 198 | return stringifyDomainPattern; 199 | })); 200 | __webpack_require__.d(__webpack_exports__, "getDomainFromUrl", (function() { 201 | return getDomainFromUrl; 202 | })); 203 | __webpack_require__.d(__webpack_exports__, "onCloseWindow", (function() { 204 | return onCloseWindow; 205 | })); 206 | __webpack_require__.d(__webpack_exports__, "isWindow", (function() { 207 | return isWindow; 208 | })); 209 | __webpack_require__.d(__webpack_exports__, "isBrowser", (function() { 210 | return isBrowser; 211 | })); 212 | __webpack_require__.d(__webpack_exports__, "isCurrentDomain", (function() { 213 | return isCurrentDomain; 214 | })); 215 | __webpack_require__.d(__webpack_exports__, "isMockDomain", (function() { 216 | return isMockDomain; 217 | })); 218 | __webpack_require__.d(__webpack_exports__, "normalizeMockUrl", (function() { 219 | return normalizeMockUrl; 220 | })); 221 | __webpack_require__.d(__webpack_exports__, "getFrameForWindow", (function() { 222 | return getFrameForWindow; 223 | })); 224 | __webpack_require__.d(__webpack_exports__, "closeWindow", (function() { 225 | return closeWindow; 226 | })); 227 | __webpack_require__.d(__webpack_exports__, "TYPES", (function() { 228 | return TYPES; 229 | })); 230 | __webpack_require__.d(__webpack_exports__, "PROTOCOL", (function() { 231 | return PROTOCOL; 232 | })); 233 | __webpack_require__.d(__webpack_exports__, "WILDCARD", (function() { 234 | return WILDCARD; 235 | })); 236 | __webpack_require__.d(__webpack_exports__, "WINDOW_TYPE", (function() { 237 | return WINDOW_TYPE; 238 | })); 239 | function isRegex(item) { 240 | return "[object RegExp]" === {}.toString.call(item); 241 | } 242 | var PROTOCOL = { 243 | MOCK: "mock:", 244 | FILE: "file:", 245 | ABOUT: "about:" 246 | }; 247 | var WILDCARD = "*"; 248 | var WINDOW_TYPE = { 249 | IFRAME: "iframe", 250 | POPUP: "popup" 251 | }; 252 | var IE_WIN_ACCESS_ERROR = "Call was rejected by callee.\r\n"; 253 | function getActualProtocol(win) { 254 | void 0 === win && (win = window); 255 | return win.location.protocol; 256 | } 257 | function getProtocol(win) { 258 | void 0 === win && (win = window); 259 | if (win.mockDomain) { 260 | var protocol = win.mockDomain.split("//")[0]; 261 | if (protocol) return protocol; 262 | } 263 | return getActualProtocol(win); 264 | } 265 | function isFileProtocol(win) { 266 | void 0 === win && (win = window); 267 | return getProtocol(win) === PROTOCOL.FILE; 268 | } 269 | function isAboutProtocol(win) { 270 | void 0 === win && (win = window); 271 | return getProtocol(win) === PROTOCOL.ABOUT; 272 | } 273 | function isMockProtocol(win) { 274 | void 0 === win && (win = window); 275 | return getProtocol(win) === PROTOCOL.MOCK; 276 | } 277 | function getParent(win) { 278 | void 0 === win && (win = window); 279 | if (win) try { 280 | if (win.parent && win.parent !== win) return win.parent; 281 | } catch (err) {} 282 | } 283 | function getOpener(win) { 284 | void 0 === win && (win = window); 285 | if (win && !getParent(win)) try { 286 | return win.opener; 287 | } catch (err) {} 288 | } 289 | function canReadFromWindow(win) { 290 | try { 291 | return !0; 292 | } catch (err) {} 293 | return !1; 294 | } 295 | function getActualDomain(win) { 296 | void 0 === win && (win = window); 297 | var location = win.location; 298 | if (!location) throw new Error("Can not read window location"); 299 | var protocol = getActualProtocol(win); 300 | if (!protocol) throw new Error("Can not read window protocol"); 301 | if (protocol === PROTOCOL.FILE) return PROTOCOL.FILE + "//"; 302 | if (protocol === PROTOCOL.ABOUT) { 303 | var parent = getParent(win); 304 | return parent && canReadFromWindow() ? getActualDomain(parent) : PROTOCOL.ABOUT + "//"; 305 | } 306 | var host = location.host; 307 | if (!host) throw new Error("Can not read window host"); 308 | return protocol + "//" + host; 309 | } 310 | function getDomain(win) { 311 | void 0 === win && (win = window); 312 | var domain = getActualDomain(win); 313 | return domain && win.mockDomain && 0 === win.mockDomain.indexOf(PROTOCOL.MOCK) ? win.mockDomain : domain; 314 | } 315 | function isBlankDomain(win) { 316 | try { 317 | if (!win.location.href) return !0; 318 | if ("about:blank" === win.location.href) return !0; 319 | } catch (err) {} 320 | return !1; 321 | } 322 | function isActuallySameDomain(win) { 323 | try { 324 | if (win === window) return !0; 325 | } catch (err) {} 326 | try { 327 | var desc = Object.getOwnPropertyDescriptor(win, "location"); 328 | if (desc && !1 === desc.enumerable) return !1; 329 | } catch (err) {} 330 | try { 331 | if (isAboutProtocol(win) && canReadFromWindow()) return !0; 332 | } catch (err) {} 333 | try { 334 | if (isMockProtocol(win) && canReadFromWindow()) return !0; 335 | } catch (err) {} 336 | try { 337 | if (getActualDomain(win) === getActualDomain(window)) return !0; 338 | } catch (err) {} 339 | return !1; 340 | } 341 | function isSameDomain(win) { 342 | if (!isActuallySameDomain(win)) return !1; 343 | try { 344 | if (win === window) return !0; 345 | if (isAboutProtocol(win) && canReadFromWindow()) return !0; 346 | if (getDomain(window) === getDomain(win)) return !0; 347 | } catch (err) {} 348 | return !1; 349 | } 350 | function assertSameDomain(win) { 351 | if (!isSameDomain(win)) throw new Error("Expected window to be same domain"); 352 | return win; 353 | } 354 | function getParents(win) { 355 | var result = []; 356 | try { 357 | for (;win.parent !== win; ) { 358 | result.push(win.parent); 359 | win = win.parent; 360 | } 361 | } catch (err) {} 362 | return result; 363 | } 364 | function isAncestorParent(parent, child) { 365 | if (!parent || !child) return !1; 366 | var childParent = getParent(child); 367 | return childParent ? childParent === parent : -1 !== getParents(child).indexOf(parent); 368 | } 369 | function getFrames(win) { 370 | var result = []; 371 | var frames; 372 | try { 373 | frames = win.frames; 374 | } catch (err) { 375 | frames = win; 376 | } 377 | var len; 378 | try { 379 | len = frames.length; 380 | } catch (err) {} 381 | if (0 === len) return result; 382 | if (len) { 383 | for (var i = 0; i < len; i++) { 384 | var frame = void 0; 385 | try { 386 | frame = frames[i]; 387 | } catch (err) { 388 | continue; 389 | } 390 | result.push(frame); 391 | } 392 | return result; 393 | } 394 | for (var _i = 0; _i < 100; _i++) { 395 | var _frame = void 0; 396 | try { 397 | _frame = frames[_i]; 398 | } catch (err) { 399 | return result; 400 | } 401 | if (!_frame) return result; 402 | result.push(_frame); 403 | } 404 | return result; 405 | } 406 | function getAllChildFrames(win) { 407 | var result = []; 408 | for (var _i3 = 0, _getFrames2 = getFrames(win); _i3 < _getFrames2.length; _i3++) { 409 | var frame = _getFrames2[_i3]; 410 | result.push(frame); 411 | for (var _i5 = 0, _getAllChildFrames2 = getAllChildFrames(frame); _i5 < _getAllChildFrames2.length; _i5++) result.push(_getAllChildFrames2[_i5]); 412 | } 413 | return result; 414 | } 415 | function getTop(win) { 416 | void 0 === win && (win = window); 417 | try { 418 | if (win.top) return win.top; 419 | } catch (err) {} 420 | if (getParent(win) === win) return win; 421 | try { 422 | if (isAncestorParent(window, win) && window.top) return window.top; 423 | } catch (err) {} 424 | try { 425 | if (isAncestorParent(win, window) && window.top) return window.top; 426 | } catch (err) {} 427 | for (var _i7 = 0, _getAllChildFrames4 = getAllChildFrames(win); _i7 < _getAllChildFrames4.length; _i7++) { 428 | var frame = _getAllChildFrames4[_i7]; 429 | try { 430 | if (frame.top) return frame.top; 431 | } catch (err) {} 432 | if (getParent(frame) === frame) return frame; 433 | } 434 | } 435 | function getNextOpener(win) { 436 | void 0 === win && (win = window); 437 | return getOpener(getTop(win) || win); 438 | } 439 | function getUltimateTop(win) { 440 | void 0 === win && (win = window); 441 | var opener = getNextOpener(win); 442 | return opener ? getUltimateTop(opener) : top; 443 | } 444 | function getAllFramesInWindow(win) { 445 | var top = getTop(win); 446 | if (!top) throw new Error("Can not determine top window"); 447 | var result = [].concat(getAllChildFrames(top), [ top ]); 448 | -1 === result.indexOf(win) && (result = [].concat(result, [ win ], getAllChildFrames(win))); 449 | return result; 450 | } 451 | function getAllWindows(win) { 452 | void 0 === win && (win = window); 453 | var frames = getAllFramesInWindow(win); 454 | var opener = getNextOpener(win); 455 | return opener ? [].concat(getAllWindows(opener), frames) : frames; 456 | } 457 | function isTop(win) { 458 | return win === getTop(win); 459 | } 460 | function isFrameWindowClosed(frame) { 461 | if (!frame.contentWindow) return !0; 462 | if (!frame.parentNode) return !0; 463 | var doc = frame.ownerDocument; 464 | if (doc && doc.documentElement && !doc.documentElement.contains(frame)) { 465 | var parent = frame; 466 | for (;parent.parentNode && parent.parentNode !== parent; ) parent = parent.parentNode; 467 | if (!parent.host || !doc.documentElement.contains(parent.host)) return !0; 468 | } 469 | return !1; 470 | } 471 | var iframeWindows = []; 472 | var iframeFrames = []; 473 | function isWindowClosed(win, allowMock) { 474 | void 0 === allowMock && (allowMock = !0); 475 | try { 476 | if (win === window) return !1; 477 | } catch (err) { 478 | return !0; 479 | } 480 | try { 481 | if (!win) return !0; 482 | } catch (err) { 483 | return !0; 484 | } 485 | try { 486 | if (win.closed) return !0; 487 | } catch (err) { 488 | return !err || err.message !== IE_WIN_ACCESS_ERROR; 489 | } 490 | if (allowMock && isSameDomain(win)) try { 491 | if (win.mockclosed) return !0; 492 | } catch (err) {} 493 | try { 494 | if (!win.parent || !win.top) return !0; 495 | } catch (err) {} 496 | var iframeIndex = function(collection, item) { 497 | for (var i = 0; i < collection.length; i++) try { 498 | if (collection[i] === item) return i; 499 | } catch (err) {} 500 | return -1; 501 | }(iframeWindows, win); 502 | if (-1 !== iframeIndex) { 503 | var frame = iframeFrames[iframeIndex]; 504 | if (frame && isFrameWindowClosed(frame)) return !0; 505 | } 506 | return !1; 507 | } 508 | function linkFrameWindow(frame) { 509 | !function() { 510 | for (var i = 0; i < iframeWindows.length; i++) { 511 | var closed = !1; 512 | try { 513 | closed = iframeWindows[i].closed; 514 | } catch (err) {} 515 | if (closed) { 516 | iframeFrames.splice(i, 1); 517 | iframeWindows.splice(i, 1); 518 | } 519 | } 520 | }(); 521 | if (frame && frame.contentWindow) try { 522 | iframeWindows.push(frame.contentWindow); 523 | iframeFrames.push(frame); 524 | } catch (err) {} 525 | } 526 | function getUserAgent(win) { 527 | return (win = win || window).navigator.mockUserAgent || win.navigator.userAgent; 528 | } 529 | function getFrameByName(win, name) { 530 | var winFrames = getFrames(win); 531 | for (var _i9 = 0; _i9 < winFrames.length; _i9++) { 532 | var childFrame = winFrames[_i9]; 533 | try { 534 | if (isSameDomain(childFrame) && childFrame.name === name && -1 !== winFrames.indexOf(childFrame)) return childFrame; 535 | } catch (err) {} 536 | } 537 | try { 538 | if (-1 !== winFrames.indexOf(win.frames[name])) return win.frames[name]; 539 | } catch (err) {} 540 | try { 541 | if (-1 !== winFrames.indexOf(win[name])) return win[name]; 542 | } catch (err) {} 543 | } 544 | function findChildFrameByName(win, name) { 545 | var frame = getFrameByName(win, name); 546 | if (frame) return frame; 547 | for (var _i11 = 0, _getFrames4 = getFrames(win); _i11 < _getFrames4.length; _i11++) { 548 | var namedFrame = findChildFrameByName(_getFrames4[_i11], name); 549 | if (namedFrame) return namedFrame; 550 | } 551 | } 552 | function findFrameByName(win, name) { 553 | return getFrameByName(win, name) || findChildFrameByName(getTop(win) || win, name); 554 | } 555 | function isParent(win, frame) { 556 | var frameParent = getParent(frame); 557 | if (frameParent) return frameParent === win; 558 | for (var _i13 = 0, _getFrames6 = getFrames(win); _i13 < _getFrames6.length; _i13++) if (_getFrames6[_i13] === frame) return !0; 559 | return !1; 560 | } 561 | function isOpener(parent, child) { 562 | return parent === getOpener(child); 563 | } 564 | function getAncestor(win) { 565 | void 0 === win && (win = window); 566 | return getOpener(win = win || window) || getParent(win) || void 0; 567 | } 568 | function getAncestors(win) { 569 | var results = []; 570 | var ancestor = win; 571 | for (;ancestor; ) (ancestor = getAncestor(ancestor)) && results.push(ancestor); 572 | return results; 573 | } 574 | function isAncestor(parent, child) { 575 | var actualParent = getAncestor(child); 576 | if (actualParent) return actualParent === parent; 577 | if (child === parent) return !1; 578 | if (getTop(child) === child) return !1; 579 | for (var _i15 = 0, _getFrames8 = getFrames(parent); _i15 < _getFrames8.length; _i15++) if (_getFrames8[_i15] === child) return !0; 580 | return !1; 581 | } 582 | function isPopup(win) { 583 | void 0 === win && (win = window); 584 | return Boolean(getOpener(win)); 585 | } 586 | function isIframe(win) { 587 | void 0 === win && (win = window); 588 | return Boolean(getParent(win)); 589 | } 590 | function isFullpage(win) { 591 | void 0 === win && (win = window); 592 | return Boolean(!isIframe(win) && !isPopup(win)); 593 | } 594 | function anyMatch(collection1, collection2) { 595 | for (var _i17 = 0; _i17 < collection1.length; _i17++) { 596 | var item1 = collection1[_i17]; 597 | for (var _i19 = 0; _i19 < collection2.length; _i19++) if (item1 === collection2[_i19]) return !0; 598 | } 599 | return !1; 600 | } 601 | function getDistanceFromTop(win) { 602 | void 0 === win && (win = window); 603 | var distance = 0; 604 | var parent = win; 605 | for (;parent; ) (parent = getParent(parent)) && (distance += 1); 606 | return distance; 607 | } 608 | function getNthParent(win, n) { 609 | void 0 === n && (n = 1); 610 | var parent = win; 611 | for (var i = 0; i < n; i++) { 612 | if (!parent) return; 613 | parent = getParent(parent); 614 | } 615 | return parent; 616 | } 617 | function getNthParentFromTop(win, n) { 618 | void 0 === n && (n = 1); 619 | return getNthParent(win, getDistanceFromTop(win) - n); 620 | } 621 | function isSameTopWindow(win1, win2) { 622 | var top1 = getTop(win1) || win1; 623 | var top2 = getTop(win2) || win2; 624 | try { 625 | if (top1 && top2) return top1 === top2; 626 | } catch (err) {} 627 | var allFrames1 = getAllFramesInWindow(win1); 628 | var allFrames2 = getAllFramesInWindow(win2); 629 | if (anyMatch(allFrames1, allFrames2)) return !0; 630 | var opener1 = getOpener(top1); 631 | var opener2 = getOpener(top2); 632 | return opener1 && anyMatch(getAllFramesInWindow(opener1), allFrames2) || opener2 && anyMatch(getAllFramesInWindow(opener2), allFrames1), 633 | !1; 634 | } 635 | function matchDomain(pattern, origin) { 636 | if ("string" == typeof pattern) { 637 | if ("string" == typeof origin) return pattern === WILDCARD || origin === pattern; 638 | if (isRegex(origin)) return !1; 639 | if (Array.isArray(origin)) return !1; 640 | } 641 | return isRegex(pattern) ? isRegex(origin) ? pattern.toString() === origin.toString() : !Array.isArray(origin) && Boolean(origin.match(pattern)) : !!Array.isArray(pattern) && (Array.isArray(origin) ? JSON.stringify(pattern) === JSON.stringify(origin) : !isRegex(origin) && pattern.some((function(subpattern) { 642 | return matchDomain(subpattern, origin); 643 | }))); 644 | } 645 | function stringifyDomainPattern(pattern) { 646 | return Array.isArray(pattern) ? "(" + pattern.join(" | ") + ")" : isRegex(pattern) ? "RegExp(" + pattern.toString() + ")" : pattern.toString(); 647 | } 648 | function getDomainFromUrl(url) { 649 | return url.match(/^(https?|mock|file):\/\//) ? url.split("/").slice(0, 3).join("/") : getDomain(); 650 | } 651 | function onCloseWindow(win, callback, delay, maxtime) { 652 | void 0 === delay && (delay = 1e3); 653 | void 0 === maxtime && (maxtime = 1 / 0); 654 | var timeout; 655 | !function check() { 656 | if (isWindowClosed(win)) { 657 | timeout && clearTimeout(timeout); 658 | return callback(); 659 | } 660 | if (maxtime <= 0) clearTimeout(timeout); else { 661 | maxtime -= delay; 662 | timeout = setTimeout(check, delay); 663 | } 664 | }(); 665 | return { 666 | cancel: function() { 667 | timeout && clearTimeout(timeout); 668 | } 669 | }; 670 | } 671 | function isWindow(obj) { 672 | try { 673 | if (obj === window) return !0; 674 | } catch (err) { 675 | if (err && err.message === IE_WIN_ACCESS_ERROR) return !0; 676 | } 677 | try { 678 | if ("[object Window]" === {}.toString.call(obj)) return !0; 679 | } catch (err) { 680 | if (err && err.message === IE_WIN_ACCESS_ERROR) return !0; 681 | } 682 | try { 683 | if (window.Window && obj instanceof window.Window) return !0; 684 | } catch (err) { 685 | if (err && err.message === IE_WIN_ACCESS_ERROR) return !0; 686 | } 687 | try { 688 | if (obj && obj.self === obj) return !0; 689 | } catch (err) { 690 | if (err && err.message === IE_WIN_ACCESS_ERROR) return !0; 691 | } 692 | try { 693 | if (obj && obj.parent === obj) return !0; 694 | } catch (err) { 695 | if (err && err.message === IE_WIN_ACCESS_ERROR) return !0; 696 | } 697 | try { 698 | if (obj && obj.top === obj) return !0; 699 | } catch (err) { 700 | if (err && err.message === IE_WIN_ACCESS_ERROR) return !0; 701 | } 702 | try { 703 | if (obj && "__unlikely_value__" === obj.__cross_domain_utils_window_check__) return !1; 704 | } catch (err) { 705 | return !0; 706 | } 707 | try { 708 | if ("postMessage" in obj && "self" in obj && "location" in obj) return !0; 709 | } catch (err) {} 710 | return !1; 711 | } 712 | function isBrowser() { 713 | return "undefined" != typeof window && void 0 !== window.location; 714 | } 715 | function isCurrentDomain(domain) { 716 | return !!isBrowser() && getDomain() === domain; 717 | } 718 | function isMockDomain(domain) { 719 | return 0 === domain.indexOf(PROTOCOL.MOCK); 720 | } 721 | function normalizeMockUrl(url) { 722 | if (!isMockDomain(getDomainFromUrl(url))) return url; 723 | throw new Error("Mock urls not supported out of test mode"); 724 | } 725 | function getFrameForWindow(win) { 726 | if (isSameDomain(win)) return assertSameDomain(win).frameElement; 727 | for (var _i21 = 0, _document$querySelect2 = document.querySelectorAll("iframe"); _i21 < _document$querySelect2.length; _i21++) { 728 | var frame = _document$querySelect2[_i21]; 729 | if (frame && frame.contentWindow && frame.contentWindow === win) return frame; 730 | } 731 | } 732 | function closeWindow(win) { 733 | if (isIframe(win)) { 734 | var frame = getFrameForWindow(win); 735 | if (frame && frame.parentElement) { 736 | frame.parentElement.removeChild(frame); 737 | return; 738 | } 739 | } 740 | try { 741 | win.close(); 742 | } catch (err) {} 743 | } 744 | var TYPES = !0; 745 | } ]); 746 | })); -------------------------------------------------------------------------------- /dist/cross-domain-utils.min.js: -------------------------------------------------------------------------------- 1 | !function(n,r){"object"==typeof exports&&"object"==typeof module?module.exports=r():"function"==typeof define&&define.amd?define("crossDomainUtils",[],r):"object"==typeof exports?exports.crossDomainUtils=r():n.crossDomainUtils=r()}("undefined"!=typeof self?self:this,(function(){return function(n){var r={};function t(e){if(r[e])return r[e].exports;var o=r[e]={i:e,l:!1,exports:{}};return n[e].call(o.exports,o,o.exports,t),o.l=!0,o.exports}return t.m=n,t.c=r,t.d=function(n,r,e){t.o(n,r)||Object.defineProperty(n,r,{enumerable:!0,get:e})},t.r=function(n){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(n,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(n,"__esModule",{value:!0})},t.t=function(n,r){if(1&r&&(n=t(n)),8&r)return n;if(4&r&&"object"==typeof n&&n&&n.__esModule)return n;var e=Object.create(null);if(t.r(e),Object.defineProperty(e,"default",{enumerable:!0,value:n}),2&r&&"string"!=typeof n)for(var o in n)t.d(e,o,function(r){return n[r]}.bind(null,o));return e},t.n=function(n){var r=n&&n.__esModule?function(){return n.default}:function(){return n};return t.d(r,"a",r),r},t.o=function(n,r){return{}.hasOwnProperty.call(n,r)},t.p="",t(t.s=0)}([function(n,r,t){"use strict";function e(n){return"[object RegExp]"==={}.toString.call(n)}t.r(r),t.d(r,"getActualProtocol",(function(){return f})),t.d(r,"getProtocol",(function(){return a})),t.d(r,"isFileProtocol",(function(){return d})),t.d(r,"isAboutProtocol",(function(){return s})),t.d(r,"isMockProtocol",(function(){return l})),t.d(r,"getParent",(function(){return w})),t.d(r,"getOpener",(function(){return m})),t.d(r,"canReadFromWindow",(function(){return v})),t.d(r,"getActualDomain",(function(){return p})),t.d(r,"getDomain",(function(){return h})),t.d(r,"isBlankDomain",(function(){return y})),t.d(r,"isActuallySameDomain",(function(){return g})),t.d(r,"isSameDomain",(function(){return O})),t.d(r,"assertSameDomain",(function(){return A})),t.d(r,"getParents",(function(){return b})),t.d(r,"isAncestorParent",(function(){return D})),t.d(r,"getFrames",(function(){return P})),t.d(r,"getAllChildFrames",(function(){return E})),t.d(r,"getTop",(function(){return W})),t.d(r,"getNextOpener",(function(){return F})),t.d(r,"getUltimateTop",(function(){return x})),t.d(r,"getAllFramesInWindow",(function(){return _})),t.d(r,"getAllWindows",(function(){return S})),t.d(r,"isTop",(function(){return T})),t.d(r,"isFrameWindowClosed",(function(){return k})),t.d(r,"isWindowClosed",(function(){return M})),t.d(r,"linkFrameWindow",(function(){return B})),t.d(r,"getUserAgent",(function(){return N})),t.d(r,"getFrameByName",(function(){return U})),t.d(r,"findChildFrameByName",(function(){return I})),t.d(r,"findFrameByName",(function(){return L})),t.d(r,"isParent",(function(){return R})),t.d(r,"isOpener",(function(){return K})),t.d(r,"getAncestor",(function(){return J})),t.d(r,"getAncestors",(function(){return Y})),t.d(r,"isAncestor",(function(){return q})),t.d(r,"isPopup",(function(){return z})),t.d(r,"isIframe",(function(){return G})),t.d(r,"isFullpage",(function(){return H})),t.d(r,"getDistanceFromTop",(function(){return V})),t.d(r,"getNthParent",(function(){return X})),t.d(r,"getNthParentFromTop",(function(){return Z})),t.d(r,"isSameTopWindow",(function(){return $})),t.d(r,"matchDomain",(function(){return nn})),t.d(r,"stringifyDomainPattern",(function(){return rn})),t.d(r,"getDomainFromUrl",(function(){return tn})),t.d(r,"onCloseWindow",(function(){return en})),t.d(r,"isWindow",(function(){return on})),t.d(r,"isBrowser",(function(){return un})),t.d(r,"isCurrentDomain",(function(){return cn})),t.d(r,"isMockDomain",(function(){return fn})),t.d(r,"normalizeMockUrl",(function(){return an})),t.d(r,"getFrameForWindow",(function(){return dn})),t.d(r,"closeWindow",(function(){return sn})),t.d(r,"TYPES",(function(){return ln})),t.d(r,"PROTOCOL",(function(){return o})),t.d(r,"WILDCARD",(function(){return i})),t.d(r,"WINDOW_TYPE",(function(){return u}));var o={MOCK:"mock:",FILE:"file:",ABOUT:"about:"},i="*",u={IFRAME:"iframe",POPUP:"popup"},c="Call was rejected by callee.\r\n";function f(n){return void 0===n&&(n=window),n.location.protocol}function a(n){if(void 0===n&&(n=window),n.mockDomain){var r=n.mockDomain.split("//")[0];if(r)return r}return f(n)}function d(n){return void 0===n&&(n=window),a(n)===o.FILE}function s(n){return void 0===n&&(n=window),a(n)===o.ABOUT}function l(n){return void 0===n&&(n=window),a(n)===o.MOCK}function w(n){if(void 0===n&&(n=window),n)try{if(n.parent&&n.parent!==n)return n.parent}catch(n){}}function m(n){if(void 0===n&&(n=window),n&&!w(n))try{return n.opener}catch(n){}}function v(n){try{return!0}catch(n){}return!1}function p(n){void 0===n&&(n=window);var r=n.location;if(!r)throw new Error("Can not read window location");var t=f(n);if(!t)throw new Error("Can not read window protocol");if(t===o.FILE)return o.FILE+"//";if(t===o.ABOUT){var e=w(n);return e&&v()?p(e):o.ABOUT+"//"}var i=r.host;if(!i)throw new Error("Can not read window host");return t+"//"+i}function h(n){void 0===n&&(n=window);var r=p(n);return r&&n.mockDomain&&0===n.mockDomain.indexOf(o.MOCK)?n.mockDomain:r}function y(n){try{if(!n.location.href)return!0;if("about:blank"===n.location.href)return!0}catch(n){}return!1}function g(n){try{if(n===window)return!0}catch(n){}try{var r=Object.getOwnPropertyDescriptor(n,"location");if(r&&!1===r.enumerable)return!1}catch(n){}try{if(s(n)&&v())return!0}catch(n){}try{if(l(n)&&v())return!0}catch(n){}try{if(p(n)===p(window))return!0}catch(n){}return!1}function O(n){if(!g(n))return!1;try{if(n===window)return!0;if(s(n)&&v())return!0;if(h(window)===h(n))return!0}catch(n){}return!1}function A(n){if(!O(n))throw new Error("Expected window to be same domain");return n}function b(n){var r=[];try{for(;n.parent!==n;)r.push(n.parent),n=n.parent}catch(n){}return r}function D(n,r){if(!n||!r)return!1;var t=w(r);return t?t===n:-1!==b(r).indexOf(n)}function P(n){var r,t,e=[];try{r=n.frames}catch(t){r=n}try{t=r.length}catch(n){}if(0===t)return e;if(t){for(var o=0;o) {\n // pass\n}\n","/* @flow */\n\nexport const PROTOCOL = {\n MOCK: (\"mock:\": \"mock:\"),\n FILE: (\"file:\": \"file:\"),\n ABOUT: (\"about:\": \"about:\"),\n};\n\nexport const WILDCARD = \"*\";\n\nexport const WINDOW_TYPE = {\n IFRAME: (\"iframe\": \"iframe\"),\n POPUP: (\"popup\": \"popup\"),\n};\n","/* @flow */\n/* eslint max-lines: 0 */\n\nimport { isRegex, noop } from \"./util\";\nimport type {\n CrossDomainWindowType,\n SameDomainWindowType,\n DomainMatcher,\n} from \"./types\";\nimport { PROTOCOL, WILDCARD } from \"./constants\";\n\nconst IE_WIN_ACCESS_ERROR = \"Call was rejected by callee.\\r\\n\";\n\nexport function getActualProtocol(win: SameDomainWindowType = window): ?string {\n return win.location.protocol;\n}\n\nexport function getProtocol(win: SameDomainWindowType = window): ?string {\n if (win.mockDomain) {\n const protocol = win.mockDomain.split(\"//\")[0];\n\n if (protocol) {\n return protocol;\n }\n }\n\n return getActualProtocol(win);\n}\n\nexport function isFileProtocol(win: SameDomainWindowType = window): boolean {\n return getProtocol(win) === PROTOCOL.FILE;\n}\n\nexport function isAboutProtocol(win: SameDomainWindowType = window): boolean {\n return getProtocol(win) === PROTOCOL.ABOUT;\n}\n\nexport function isMockProtocol(win: SameDomainWindowType = window): boolean {\n return getProtocol(win) === PROTOCOL.MOCK;\n}\n\nexport function getParent(\n win?: CrossDomainWindowType = window\n): ?CrossDomainWindowType {\n if (!win) {\n return;\n }\n\n try {\n if (win.parent && win.parent !== win) {\n return win.parent;\n }\n } catch (err) {\n // pass\n }\n}\n\nexport function getOpener(\n win?: CrossDomainWindowType = window\n): ?CrossDomainWindowType {\n if (!win) {\n return;\n }\n\n // Make sure we're not actually an iframe which has had window.open() called on us\n if (getParent(win)) {\n return;\n }\n\n try {\n return win.opener;\n } catch (err) {\n // pass\n }\n}\n\nexport function canReadFromWindow(\n win: CrossDomainWindowType | SameDomainWindowType\n): boolean {\n try {\n // $FlowFixMe\n noop(win && win.location && win.location.href);\n return true;\n } catch (err) {\n // pass\n }\n\n return false;\n}\n\nexport function getActualDomain(win?: SameDomainWindowType = window): string {\n const location = win.location;\n\n if (!location) {\n throw new Error(`Can not read window location`);\n }\n\n const protocol = getActualProtocol(win);\n\n if (!protocol) {\n throw new Error(`Can not read window protocol`);\n }\n\n if (protocol === PROTOCOL.FILE) {\n return `${PROTOCOL.FILE}//`;\n }\n\n if (protocol === PROTOCOL.ABOUT) {\n const parent = getParent(win);\n if (parent && canReadFromWindow(parent)) {\n // $FlowFixMe\n return getActualDomain(parent);\n }\n\n return `${PROTOCOL.ABOUT}//`;\n }\n\n const host = location.host;\n\n if (!host) {\n throw new Error(`Can not read window host`);\n }\n\n return `${protocol}//${host}`;\n}\n\nexport function getDomain(win?: SameDomainWindowType = window): string {\n const domain = getActualDomain(win);\n\n if (domain && win.mockDomain && win.mockDomain.indexOf(PROTOCOL.MOCK) === 0) {\n return win.mockDomain;\n }\n\n return domain;\n}\n\nexport function isBlankDomain(win: CrossDomainWindowType): boolean {\n try {\n // $FlowFixMe\n if (!win.location.href) {\n return true;\n }\n\n if (win.location.href === \"about:blank\") {\n return true;\n }\n } catch (err) {\n // pass\n }\n\n return false;\n}\n\nexport function isActuallySameDomain(win: CrossDomainWindowType): boolean {\n try {\n if (win === window) {\n return true;\n }\n } catch (err) {\n // pass\n }\n\n try {\n const desc = Object.getOwnPropertyDescriptor(win, \"location\");\n\n if (desc && desc.enumerable === false) {\n return false;\n }\n } catch (err) {\n // pass\n }\n\n try {\n // $FlowFixMe\n if (isAboutProtocol(win) && canReadFromWindow(win)) {\n return true;\n }\n } catch (err) {\n // pass\n }\n\n try {\n // $FlowFixMe\n if (isMockProtocol(win) && canReadFromWindow(win)) {\n return true;\n }\n } catch (err) {\n // pass\n }\n\n try {\n // $FlowFixMe\n if (getActualDomain(win) === getActualDomain(window)) {\n return true;\n }\n } catch (err) {\n // pass\n }\n\n return false;\n}\n\nexport function isSameDomain(\n win: CrossDomainWindowType | SameDomainWindowType\n): boolean {\n if (!isActuallySameDomain(win)) {\n return false;\n }\n\n try {\n if (win === window) {\n return true;\n }\n\n // $FlowFixMe\n if (isAboutProtocol(win) && canReadFromWindow(win)) {\n return true;\n }\n\n // $FlowFixMe\n if (getDomain(window) === getDomain(win)) {\n return true;\n }\n } catch (err) {\n // pass\n }\n\n return false;\n}\n\nexport function assertSameDomain(\n win: CrossDomainWindowType | SameDomainWindowType\n): SameDomainWindowType {\n if (!isSameDomain(win)) {\n throw new Error(`Expected window to be same domain`);\n }\n\n // $FlowFixMe\n return win;\n}\n\nexport function getParents(\n win: CrossDomainWindowType\n): $ReadOnlyArray {\n const result = [];\n\n try {\n while (win.parent !== win) {\n result.push(win.parent);\n win = win.parent;\n }\n } catch (err) {\n // pass\n }\n\n return result;\n}\n\nexport function isAncestorParent(\n parent: CrossDomainWindowType,\n child: CrossDomainWindowType\n): boolean {\n if (!parent || !child) {\n return false;\n }\n\n const childParent = getParent(child);\n\n if (childParent) {\n return childParent === parent;\n }\n\n if (getParents(child).indexOf(parent) !== -1) {\n return true;\n }\n\n return false;\n}\n\nexport function getFrames(\n win: CrossDomainWindowType\n): $ReadOnlyArray {\n const result = [];\n\n let frames;\n\n try {\n frames = win.frames;\n } catch (err) {\n frames = win;\n }\n\n let len;\n\n try {\n len = frames.length;\n } catch (err) {\n // pass\n }\n\n if (len === 0) {\n return result;\n }\n\n if (len) {\n for (let i = 0; i < len; i++) {\n let frame;\n\n try {\n frame = frames[i];\n } catch (err) {\n continue;\n }\n\n result.push(frame);\n }\n\n return result;\n }\n\n for (let i = 0; i < 100; i++) {\n let frame;\n\n try {\n frame = frames[i];\n } catch (err) {\n return result;\n }\n\n if (!frame) {\n return result;\n }\n\n result.push(frame);\n }\n\n return result;\n}\n\nexport function getAllChildFrames(\n win: CrossDomainWindowType\n): $ReadOnlyArray {\n const result = [];\n\n for (const frame of getFrames(win)) {\n result.push(frame);\n\n for (const childFrame of getAllChildFrames(frame)) {\n result.push(childFrame);\n }\n }\n\n return result;\n}\n\nexport function getTop(\n win?: CrossDomainWindowType = window\n): ?CrossDomainWindowType {\n try {\n if (win.top) {\n return win.top;\n }\n } catch (err) {\n // pass\n }\n\n if (getParent(win) === win) {\n return win;\n }\n\n try {\n if (isAncestorParent(window, win) && window.top) {\n return window.top;\n }\n } catch (err) {\n // pass\n }\n\n try {\n if (isAncestorParent(win, window) && window.top) {\n return window.top;\n }\n } catch (err) {\n // pass\n }\n\n for (const frame of getAllChildFrames(win)) {\n try {\n if (frame.top) {\n return frame.top;\n }\n } catch (err) {\n // pass\n }\n\n if (getParent(frame) === frame) {\n return frame;\n }\n }\n}\n\nexport function getNextOpener(\n win?: CrossDomainWindowType = window\n): ?CrossDomainWindowType {\n return getOpener(getTop(win) || win);\n}\n\nexport function getUltimateTop(\n win?: CrossDomainWindowType = window\n): CrossDomainWindowType {\n const opener = getNextOpener(win);\n\n if (opener) {\n return getUltimateTop(opener);\n }\n\n return top;\n}\n\nexport function getAllFramesInWindow(\n win: CrossDomainWindowType\n): $ReadOnlyArray {\n const top = getTop(win);\n\n if (!top) {\n throw new Error(`Can not determine top window`);\n }\n\n let result = [...getAllChildFrames(top), top];\n\n // Win may be in shadow dom\n if (result.indexOf(win) === -1) {\n result = [...result, win, ...getAllChildFrames(win)];\n }\n\n return result;\n}\n\nexport function getAllWindows(\n win?: CrossDomainWindowType = window\n): $ReadOnlyArray {\n const frames = getAllFramesInWindow(win);\n const opener = getNextOpener(win);\n\n if (opener) {\n return [...getAllWindows(opener), ...frames];\n } else {\n return frames;\n }\n}\n\nexport function isTop(win: CrossDomainWindowType): boolean {\n return win === getTop(win);\n}\n\nexport function isFrameWindowClosed(frame: HTMLIFrameElement): boolean {\n if (!frame.contentWindow) {\n return true;\n }\n\n if (!frame.parentNode) {\n return true;\n }\n\n const doc = frame.ownerDocument;\n\n if (doc && doc.documentElement && !doc.documentElement.contains(frame)) {\n let parent = frame;\n\n while (parent.parentNode && parent.parentNode !== parent) {\n parent = parent.parentNode;\n }\n\n // $FlowFixMe\n if (!parent.host || !doc.documentElement.contains(parent.host)) {\n return true;\n }\n }\n\n return false;\n}\n\nfunction safeIndexOf(collection: $ReadOnlyArray, item: T): number {\n for (let i = 0; i < collection.length; i++) {\n try {\n if (collection[i] === item) {\n return i;\n }\n } catch (err) {\n // pass\n }\n }\n\n return -1;\n}\n\nconst iframeWindows = [];\nconst iframeFrames = [];\n\nexport function isWindowClosed(\n win: CrossDomainWindowType,\n allowMock: boolean = true\n): boolean {\n try {\n if (win === window) {\n return false;\n }\n } catch (err) {\n return true;\n }\n\n try {\n if (!win) {\n return true;\n }\n } catch (err) {\n return true;\n }\n\n try {\n if (win.closed) {\n return true;\n }\n } catch (err) {\n // I love you so much IE\n\n if (err && err.message === IE_WIN_ACCESS_ERROR) {\n return false;\n }\n\n return true;\n }\n\n if (allowMock && isSameDomain(win)) {\n try {\n // $FlowFixMe\n if (win.mockclosed) {\n return true;\n }\n } catch (err) {\n // pass\n }\n }\n\n // Mobile safari\n\n try {\n if (!win.parent || !win.top) {\n return true;\n }\n } catch (err) {\n // pass\n }\n\n // Yes, this actually happens in IE. win === win errors out when the window\n // is from an iframe, and the iframe was removed from the page.\n\n try {\n noop(win === win); // eslint-disable-line no-self-compare\n } catch (err) {\n return true;\n }\n\n // IE orphaned frame\n\n const iframeIndex = safeIndexOf(iframeWindows, win);\n\n if (iframeIndex !== -1) {\n const frame = iframeFrames[iframeIndex];\n\n if (frame && isFrameWindowClosed(frame)) {\n return true;\n }\n }\n\n return false;\n}\n\nfunction cleanIframes() {\n for (let i = 0; i < iframeWindows.length; i++) {\n let closed = false;\n\n try {\n closed = iframeWindows[i].closed;\n } catch (err) {\n // pass\n }\n\n if (closed) {\n iframeFrames.splice(i, 1);\n iframeWindows.splice(i, 1);\n }\n }\n}\n\nexport function linkFrameWindow(frame: HTMLIFrameElement) {\n cleanIframes();\n\n if (frame && frame.contentWindow) {\n try {\n iframeWindows.push(frame.contentWindow);\n iframeFrames.push(frame);\n } catch (err) {\n // pass\n }\n }\n}\n\nexport function getUserAgent(win: ?SameDomainWindowType): string {\n win = win || window;\n return win.navigator.mockUserAgent || win.navigator.userAgent;\n}\n\nexport function getFrameByName(\n win: CrossDomainWindowType,\n name: string\n): ?CrossDomainWindowType {\n const winFrames = getFrames(win);\n\n for (const childFrame of winFrames) {\n try {\n if (\n isSameDomain(childFrame) &&\n // $FlowFixMe\n childFrame.name === name &&\n winFrames.indexOf(childFrame) !== -1\n ) {\n return childFrame;\n }\n } catch (err) {\n // pass\n }\n }\n\n try {\n // $FlowFixMe\n if (winFrames.indexOf(win.frames[name]) !== -1) {\n // $FlowFixMe\n return win.frames[name];\n }\n } catch (err) {\n // pass\n }\n\n try {\n if (winFrames.indexOf(win[name]) !== -1) {\n return win[name];\n }\n } catch (err) {\n // pass\n }\n}\n\nexport function findChildFrameByName(\n win: CrossDomainWindowType,\n name: string\n): ?CrossDomainWindowType {\n const frame = getFrameByName(win, name);\n\n if (frame) {\n return frame;\n }\n\n for (const childFrame of getFrames(win)) {\n const namedFrame = findChildFrameByName(childFrame, name);\n\n if (namedFrame) {\n return namedFrame;\n }\n }\n}\n\nexport function findFrameByName(\n win: CrossDomainWindowType,\n name: string\n): ?CrossDomainWindowType {\n const frame = getFrameByName(win, name);\n\n if (frame) {\n return frame;\n }\n\n const top = getTop(win) || win;\n\n return findChildFrameByName(top, name);\n}\n\nexport function isParent(\n win: CrossDomainWindowType,\n frame: CrossDomainWindowType\n): boolean {\n const frameParent = getParent(frame);\n\n if (frameParent) {\n return frameParent === win;\n }\n\n for (const childFrame of getFrames(win)) {\n if (childFrame === frame) {\n return true;\n }\n }\n\n return false;\n}\n\nexport function isOpener(\n parent: CrossDomainWindowType,\n child: CrossDomainWindowType\n): boolean {\n return parent === getOpener(child);\n}\n\nexport function getAncestor(\n win?: CrossDomainWindowType = window\n): ?CrossDomainWindowType {\n win = win || window;\n\n const opener = getOpener(win);\n\n if (opener) {\n return opener;\n }\n\n const parent = getParent(win);\n\n if (parent) {\n return parent;\n }\n}\n\nexport function getAncestors(\n win: CrossDomainWindowType\n): $ReadOnlyArray {\n const results = [];\n\n let ancestor = win;\n\n while (ancestor) {\n ancestor = getAncestor(ancestor);\n if (ancestor) {\n results.push(ancestor);\n }\n }\n\n return results;\n}\n\nexport function isAncestor(\n parent: CrossDomainWindowType,\n child: CrossDomainWindowType\n): boolean {\n const actualParent = getAncestor(child);\n\n if (actualParent) {\n if (actualParent === parent) {\n return true;\n }\n\n return false;\n }\n\n if (child === parent) {\n return false;\n }\n\n if (getTop(child) === child) {\n return false;\n }\n\n for (const frame of getFrames(parent)) {\n if (frame === child) {\n return true;\n }\n }\n\n return false;\n}\n\nexport function isPopup(win?: CrossDomainWindowType = window): boolean {\n return Boolean(getOpener(win));\n}\n\nexport function isIframe(win?: CrossDomainWindowType = window): boolean {\n return Boolean(getParent(win));\n}\n\nexport function isFullpage(win?: CrossDomainWindowType = window): boolean {\n return Boolean(!isIframe(win) && !isPopup(win));\n}\n\nfunction anyMatch(collection1, collection2): boolean {\n for (const item1 of collection1) {\n for (const item2 of collection2) {\n if (item1 === item2) {\n return true;\n }\n }\n }\n\n return false;\n}\n\nexport function getDistanceFromTop(\n win: CrossDomainWindowType = window\n): number {\n let distance = 0;\n let parent = win;\n\n while (parent) {\n parent = getParent(parent);\n if (parent) {\n distance += 1;\n }\n }\n\n return distance;\n}\n\nexport function getNthParent(\n win: CrossDomainWindowType,\n n: number = 1\n): ?CrossDomainWindowType {\n let parent = win;\n\n for (let i = 0; i < n; i++) {\n if (!parent) {\n return;\n }\n\n parent = getParent(parent);\n }\n\n return parent;\n}\n\nexport function getNthParentFromTop(\n win: CrossDomainWindowType,\n n: number = 1\n): ?CrossDomainWindowType {\n return getNthParent(win, getDistanceFromTop(win) - n);\n}\n\nexport function isSameTopWindow(\n win1: CrossDomainWindowType,\n win2: CrossDomainWindowType\n): boolean {\n const top1 = getTop(win1) || win1;\n const top2 = getTop(win2) || win2;\n\n try {\n if (top1 && top2) {\n if (top1 === top2) {\n return true;\n }\n\n return false;\n }\n } catch (err) {\n // pass\n }\n\n const allFrames1 = getAllFramesInWindow(win1);\n const allFrames2 = getAllFramesInWindow(win2);\n\n if (anyMatch(allFrames1, allFrames2)) {\n return true;\n }\n\n const opener1 = getOpener(top1);\n const opener2 = getOpener(top2);\n\n if (opener1 && anyMatch(getAllFramesInWindow(opener1), allFrames2)) {\n return false;\n }\n\n if (opener2 && anyMatch(getAllFramesInWindow(opener2), allFrames1)) {\n return false;\n }\n\n return false;\n}\n\nexport function matchDomain(\n pattern: DomainMatcher,\n origin: DomainMatcher\n): boolean {\n if (typeof pattern === \"string\") {\n if (typeof origin === \"string\") {\n return pattern === WILDCARD || origin === pattern;\n }\n\n if (isRegex(origin)) {\n return false;\n }\n\n if (Array.isArray(origin)) {\n return false;\n }\n }\n\n if (isRegex(pattern)) {\n if (isRegex(origin)) {\n return pattern.toString() === origin.toString();\n }\n\n if (Array.isArray(origin)) {\n return false;\n }\n\n // $FlowFixMe\n return Boolean(origin.match(pattern));\n }\n\n if (Array.isArray(pattern)) {\n if (Array.isArray(origin)) {\n return JSON.stringify(pattern) === JSON.stringify(origin);\n }\n\n if (isRegex(origin)) {\n return false;\n }\n\n return pattern.some((subpattern) => matchDomain(subpattern, origin));\n }\n\n return false;\n}\n\nexport function stringifyDomainPattern(pattern: DomainMatcher): string {\n if (Array.isArray(pattern)) {\n return `(${pattern.join(\" | \")})`;\n } else if (isRegex(pattern)) {\n return `RegExp(${pattern.toString()})`;\n } else {\n return pattern.toString();\n }\n}\n\nexport function getDomainFromUrl(url: string): string {\n let domain;\n\n if (url.match(/^(https?|mock|file):\\/\\//)) {\n domain = url;\n } else {\n return getDomain();\n }\n\n domain = domain.split(\"/\").slice(0, 3).join(\"/\");\n\n return domain;\n}\n\nexport function onCloseWindow(\n win: CrossDomainWindowType,\n callback: Function,\n delay: number = 1000,\n maxtime: number = Infinity\n): {| cancel: () => void |} {\n let timeout;\n\n const check = () => {\n if (isWindowClosed(win)) {\n if (timeout) {\n clearTimeout(timeout);\n }\n\n return callback();\n }\n\n if (maxtime <= 0) {\n clearTimeout(timeout);\n } else {\n maxtime -= delay;\n timeout = setTimeout(check, delay);\n }\n };\n\n check();\n\n return {\n cancel() {\n if (timeout) {\n clearTimeout(timeout);\n }\n },\n };\n}\n\n// eslint-disable-next-line complexity\nexport function isWindow(obj: Object): boolean {\n try {\n if (obj === window) {\n return true;\n }\n } catch (err) {\n if (err && err.message === IE_WIN_ACCESS_ERROR) {\n return true;\n }\n }\n\n try {\n // $FlowFixMe method-unbinding\n if (Object.prototype.toString.call(obj) === \"[object Window]\") {\n return true;\n }\n } catch (err) {\n if (err && err.message === IE_WIN_ACCESS_ERROR) {\n return true;\n }\n }\n\n try {\n if (window.Window && obj instanceof window.Window) {\n return true;\n }\n } catch (err) {\n if (err && err.message === IE_WIN_ACCESS_ERROR) {\n return true;\n }\n }\n\n try {\n if (obj && obj.self === obj) {\n return true;\n }\n } catch (err) {\n if (err && err.message === IE_WIN_ACCESS_ERROR) {\n return true;\n }\n }\n\n try {\n if (obj && obj.parent === obj) {\n return true;\n }\n } catch (err) {\n if (err && err.message === IE_WIN_ACCESS_ERROR) {\n return true;\n }\n }\n\n try {\n if (obj && obj.top === obj) {\n return true;\n }\n } catch (err) {\n if (err && err.message === IE_WIN_ACCESS_ERROR) {\n return true;\n }\n }\n\n try {\n // eslint-disable-next-line no-self-compare\n if (noop(obj === obj) === \"__unlikely_value__\") {\n return false;\n }\n } catch (err) {\n return true;\n }\n\n try {\n if (\n obj &&\n obj.__cross_domain_utils_window_check__ === \"__unlikely_value__\"\n ) {\n return false;\n }\n } catch (err) {\n return true;\n }\n\n try {\n if (\"postMessage\" in obj && \"self\" in obj && \"location\" in obj) {\n return true;\n }\n } catch (err) {\n // pass\n }\n\n return false;\n}\n\nexport function isBrowser(): boolean {\n return (\n typeof window !== \"undefined\" && typeof window.location !== \"undefined\"\n );\n}\n\nexport function isCurrentDomain(domain: string): boolean {\n if (!isBrowser()) {\n return false;\n }\n\n return getDomain() === domain;\n}\n\nexport function isMockDomain(domain: string): boolean {\n return domain.indexOf(PROTOCOL.MOCK) === 0;\n}\n\nexport function normalizeMockUrl(url: string): string {\n if (!isMockDomain(getDomainFromUrl(url))) {\n return url;\n }\n\n if (!__TEST__) {\n throw new Error(`Mock urls not supported out of test mode`);\n }\n\n return url.replace(/^mock:\\/\\/[^/]+/, getActualDomain(window));\n}\n\nexport function getFrameForWindow(win: CrossDomainWindowType): ?HTMLElement {\n if (isSameDomain(win)) {\n return assertSameDomain(win).frameElement;\n }\n\n for (const frame of document.querySelectorAll(\"iframe\")) {\n if (frame && frame.contentWindow && frame.contentWindow === win) {\n return frame;\n }\n }\n}\n\nexport function closeWindow(win: CrossDomainWindowType) {\n if (isIframe(win)) {\n const frame = getFrameForWindow(win);\n if (frame && frame.parentElement) {\n frame.parentElement.removeChild(frame);\n return;\n }\n }\n\n try {\n win.close();\n } catch (err) {\n // pass\n }\n}\n","/* @flow */\n\n// export something to force webpack to see this as an ES module\nexport const TYPES = true;\n\nexport type CrossDomainLocationType = {||};\n\nexport type CrossDomainWindowType = {|\n location: string | CrossDomainLocationType,\n self: CrossDomainWindowType,\n closed: boolean,\n open: (string, string, string) => CrossDomainWindowType,\n close: () => void,\n focus: () => void,\n top: CrossDomainWindowType,\n frames: $ReadOnlyArray,\n opener?: CrossDomainWindowType,\n parent: CrossDomainWindowType,\n length: number,\n postMessage: (string, string) => void,\n|};\n\nexport type SameDomainWindowType = Object & {|\n location: string | Object,\n self: CrossDomainWindowType,\n closed: boolean,\n open: (string, string, string) => CrossDomainWindowType,\n close: () => void,\n focus: () => void,\n XMLHttpRequest: typeof XMLHttpRequest,\n document: Document,\n navigator: {|\n userAgent: string,\n mockUserAgent?: string,\n |},\n|};\n\nexport type DomainMatcher = string | $ReadOnlyArray | RegExp;\n"],"sourceRoot":""} -------------------------------------------------------------------------------- /dist/module/constants.js: -------------------------------------------------------------------------------- 1 | export var PROTOCOL = { 2 | MOCK: "mock:", 3 | FILE: "file:", 4 | ABOUT: "about:" 5 | }; 6 | export var WILDCARD = "*"; 7 | export var WINDOW_TYPE = { 8 | IFRAME: "iframe", 9 | POPUP: "popup" 10 | }; -------------------------------------------------------------------------------- /dist/module/index.flow.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krakenjs/cross-domain-utils/8f7bb2d9e07c465a5c625724b980f1f551dd28fa/dist/module/index.flow.js -------------------------------------------------------------------------------- /dist/module/index.js: -------------------------------------------------------------------------------- 1 | export * from "./utils"; 2 | export * from "./types"; 3 | export * from "./constants"; -------------------------------------------------------------------------------- /dist/module/types.js: -------------------------------------------------------------------------------- 1 | export var TYPES = true; -------------------------------------------------------------------------------- /dist/module/util.js: -------------------------------------------------------------------------------- 1 | export function isRegex(item) { 2 | return Object.prototype.toString.call(item) === "[object RegExp]"; 3 | } 4 | export function noop() {} -------------------------------------------------------------------------------- /dist/module/utils.js: -------------------------------------------------------------------------------- 1 | import { isRegex, noop } from "./util"; 2 | import { PROTOCOL, WILDCARD } from "./constants"; 3 | var IE_WIN_ACCESS_ERROR = "Call was rejected by callee.\r\n"; 4 | export function getActualProtocol(win) { 5 | if (win === void 0) { 6 | win = window; 7 | } 8 | return win.location.protocol; 9 | } 10 | export function getProtocol(win) { 11 | if (win === void 0) { 12 | win = window; 13 | } 14 | if (win.mockDomain) { 15 | var protocol = win.mockDomain.split("//")[0]; 16 | if (protocol) { 17 | return protocol; 18 | } 19 | } 20 | return getActualProtocol(win); 21 | } 22 | export function isFileProtocol(win) { 23 | if (win === void 0) { 24 | win = window; 25 | } 26 | return getProtocol(win) === PROTOCOL.FILE; 27 | } 28 | export function isAboutProtocol(win) { 29 | if (win === void 0) { 30 | win = window; 31 | } 32 | return getProtocol(win) === PROTOCOL.ABOUT; 33 | } 34 | export function isMockProtocol(win) { 35 | if (win === void 0) { 36 | win = window; 37 | } 38 | return getProtocol(win) === PROTOCOL.MOCK; 39 | } 40 | export function getParent(win) { 41 | if (win === void 0) { 42 | win = window; 43 | } 44 | if (!win) { 45 | return; 46 | } 47 | try { 48 | if (win.parent && win.parent !== win) { 49 | return win.parent; 50 | } 51 | } catch (err) {} 52 | } 53 | export function getOpener(win) { 54 | if (win === void 0) { 55 | win = window; 56 | } 57 | if (!win) { 58 | return; 59 | } 60 | if (getParent(win)) { 61 | return; 62 | } 63 | try { 64 | return win.opener; 65 | } catch (err) {} 66 | } 67 | export function canReadFromWindow(win) { 68 | try { 69 | noop(win && win.location && win.location.href); 70 | return true; 71 | } catch (err) {} 72 | return false; 73 | } 74 | export function getActualDomain(win) { 75 | if (win === void 0) { 76 | win = window; 77 | } 78 | var location = win.location; 79 | if (!location) { 80 | throw new Error("Can not read window location"); 81 | } 82 | var protocol = getActualProtocol(win); 83 | if (!protocol) { 84 | throw new Error("Can not read window protocol"); 85 | } 86 | if (protocol === PROTOCOL.FILE) { 87 | return PROTOCOL.FILE + "//"; 88 | } 89 | if (protocol === PROTOCOL.ABOUT) { 90 | var parent = getParent(win); 91 | if (parent && canReadFromWindow(parent)) { 92 | return getActualDomain(parent); 93 | } 94 | return PROTOCOL.ABOUT + "//"; 95 | } 96 | var host = location.host; 97 | if (!host) { 98 | throw new Error("Can not read window host"); 99 | } 100 | return protocol + "//" + host; 101 | } 102 | export function getDomain(win) { 103 | if (win === void 0) { 104 | win = window; 105 | } 106 | var domain = getActualDomain(win); 107 | if (domain && win.mockDomain && win.mockDomain.indexOf(PROTOCOL.MOCK) === 0) { 108 | return win.mockDomain; 109 | } 110 | return domain; 111 | } 112 | export function isBlankDomain(win) { 113 | try { 114 | if (!win.location.href) { 115 | return true; 116 | } 117 | if (win.location.href === "about:blank") { 118 | return true; 119 | } 120 | } catch (err) {} 121 | return false; 122 | } 123 | export function isActuallySameDomain(win) { 124 | try { 125 | if (win === window) { 126 | return true; 127 | } 128 | } catch (err) {} 129 | try { 130 | var desc = Object.getOwnPropertyDescriptor(win, "location"); 131 | if (desc && desc.enumerable === false) { 132 | return false; 133 | } 134 | } catch (err) {} 135 | try { 136 | if (isAboutProtocol(win) && canReadFromWindow(win)) { 137 | return true; 138 | } 139 | } catch (err) {} 140 | try { 141 | if (isMockProtocol(win) && canReadFromWindow(win)) { 142 | return true; 143 | } 144 | } catch (err) {} 145 | try { 146 | if (getActualDomain(win) === getActualDomain(window)) { 147 | return true; 148 | } 149 | } catch (err) {} 150 | return false; 151 | } 152 | export function isSameDomain(win) { 153 | if (!isActuallySameDomain(win)) { 154 | return false; 155 | } 156 | try { 157 | if (win === window) { 158 | return true; 159 | } 160 | if (isAboutProtocol(win) && canReadFromWindow(win)) { 161 | return true; 162 | } 163 | if (getDomain(window) === getDomain(win)) { 164 | return true; 165 | } 166 | } catch (err) {} 167 | return false; 168 | } 169 | export function assertSameDomain(win) { 170 | if (!isSameDomain(win)) { 171 | throw new Error("Expected window to be same domain"); 172 | } 173 | return win; 174 | } 175 | export function getParents(win) { 176 | var result = []; 177 | try { 178 | while (win.parent !== win) { 179 | result.push(win.parent); 180 | win = win.parent; 181 | } 182 | } catch (err) {} 183 | return result; 184 | } 185 | export function isAncestorParent(parent, child) { 186 | if (!parent || !child) { 187 | return false; 188 | } 189 | var childParent = getParent(child); 190 | if (childParent) { 191 | return childParent === parent; 192 | } 193 | if (getParents(child).indexOf(parent) !== -1) { 194 | return true; 195 | } 196 | return false; 197 | } 198 | export function getFrames(win) { 199 | var result = []; 200 | var frames; 201 | try { 202 | frames = win.frames; 203 | } catch (err) { 204 | frames = win; 205 | } 206 | var len; 207 | try { 208 | len = frames.length; 209 | } catch (err) {} 210 | if (len === 0) { 211 | return result; 212 | } 213 | if (len) { 214 | for (var i = 0; i < len; i++) { 215 | var frame = void 0; 216 | try { 217 | frame = frames[i]; 218 | } catch (err) { 219 | continue; 220 | } 221 | result.push(frame); 222 | } 223 | return result; 224 | } 225 | for (var _i = 0; _i < 100; _i++) { 226 | var _frame = void 0; 227 | try { 228 | _frame = frames[_i]; 229 | } catch (err) { 230 | return result; 231 | } 232 | if (!_frame) { 233 | return result; 234 | } 235 | result.push(_frame); 236 | } 237 | return result; 238 | } 239 | export function getAllChildFrames(win) { 240 | var result = []; 241 | for (var _i3 = 0, _getFrames2 = getFrames(win); _i3 < _getFrames2.length; _i3++) { 242 | var frame = _getFrames2[_i3]; 243 | result.push(frame); 244 | for (var _i5 = 0, _getAllChildFrames2 = getAllChildFrames(frame); _i5 < _getAllChildFrames2.length; _i5++) { 245 | var childFrame = _getAllChildFrames2[_i5]; 246 | result.push(childFrame); 247 | } 248 | } 249 | return result; 250 | } 251 | export function getTop(win) { 252 | if (win === void 0) { 253 | win = window; 254 | } 255 | try { 256 | if (win.top) { 257 | return win.top; 258 | } 259 | } catch (err) {} 260 | if (getParent(win) === win) { 261 | return win; 262 | } 263 | try { 264 | if (isAncestorParent(window, win) && window.top) { 265 | return window.top; 266 | } 267 | } catch (err) {} 268 | try { 269 | if (isAncestorParent(win, window) && window.top) { 270 | return window.top; 271 | } 272 | } catch (err) {} 273 | for (var _i7 = 0, _getAllChildFrames4 = getAllChildFrames(win); _i7 < _getAllChildFrames4.length; _i7++) { 274 | var frame = _getAllChildFrames4[_i7]; 275 | try { 276 | if (frame.top) { 277 | return frame.top; 278 | } 279 | } catch (err) {} 280 | if (getParent(frame) === frame) { 281 | return frame; 282 | } 283 | } 284 | } 285 | export function getNextOpener(win) { 286 | if (win === void 0) { 287 | win = window; 288 | } 289 | return getOpener(getTop(win) || win); 290 | } 291 | export function getUltimateTop(win) { 292 | if (win === void 0) { 293 | win = window; 294 | } 295 | var opener = getNextOpener(win); 296 | if (opener) { 297 | return getUltimateTop(opener); 298 | } 299 | return top; 300 | } 301 | export function getAllFramesInWindow(win) { 302 | var top = getTop(win); 303 | if (!top) { 304 | throw new Error("Can not determine top window"); 305 | } 306 | var result = [].concat(getAllChildFrames(top), [top]); 307 | if (result.indexOf(win) === -1) { 308 | result = [].concat(result, [win], getAllChildFrames(win)); 309 | } 310 | return result; 311 | } 312 | export function getAllWindows(win) { 313 | if (win === void 0) { 314 | win = window; 315 | } 316 | var frames = getAllFramesInWindow(win); 317 | var opener = getNextOpener(win); 318 | if (opener) { 319 | return [].concat(getAllWindows(opener), frames); 320 | } else { 321 | return frames; 322 | } 323 | } 324 | export function isTop(win) { 325 | return win === getTop(win); 326 | } 327 | export function isFrameWindowClosed(frame) { 328 | if (!frame.contentWindow) { 329 | return true; 330 | } 331 | if (!frame.parentNode) { 332 | return true; 333 | } 334 | var doc = frame.ownerDocument; 335 | if (doc && doc.documentElement && !doc.documentElement.contains(frame)) { 336 | var parent = frame; 337 | while (parent.parentNode && parent.parentNode !== parent) { 338 | parent = parent.parentNode; 339 | } 340 | if (!parent.host || !doc.documentElement.contains(parent.host)) { 341 | return true; 342 | } 343 | } 344 | return false; 345 | } 346 | function safeIndexOf(collection, item) { 347 | for (var i = 0; i < collection.length; i++) { 348 | try { 349 | if (collection[i] === item) { 350 | return i; 351 | } 352 | } catch (err) {} 353 | } 354 | return -1; 355 | } 356 | var iframeWindows = []; 357 | var iframeFrames = []; 358 | export function isWindowClosed(win, allowMock) { 359 | if (allowMock === void 0) { 360 | allowMock = true; 361 | } 362 | try { 363 | if (win === window) { 364 | return false; 365 | } 366 | } catch (err) { 367 | return true; 368 | } 369 | try { 370 | if (!win) { 371 | return true; 372 | } 373 | } catch (err) { 374 | return true; 375 | } 376 | try { 377 | if (win.closed) { 378 | return true; 379 | } 380 | } catch (err) { 381 | if (err && err.message === IE_WIN_ACCESS_ERROR) { 382 | return false; 383 | } 384 | return true; 385 | } 386 | if (allowMock && isSameDomain(win)) { 387 | try { 388 | if (win.mockclosed) { 389 | return true; 390 | } 391 | } catch (err) {} 392 | } 393 | try { 394 | if (!win.parent || !win.top) { 395 | return true; 396 | } 397 | } catch (err) {} 398 | try { 399 | noop(win === win); 400 | } catch (err) { 401 | return true; 402 | } 403 | var iframeIndex = safeIndexOf(iframeWindows, win); 404 | if (iframeIndex !== -1) { 405 | var frame = iframeFrames[iframeIndex]; 406 | if (frame && isFrameWindowClosed(frame)) { 407 | return true; 408 | } 409 | } 410 | return false; 411 | } 412 | function cleanIframes() { 413 | for (var i = 0; i < iframeWindows.length; i++) { 414 | var closed = false; 415 | try { 416 | closed = iframeWindows[i].closed; 417 | } catch (err) {} 418 | if (closed) { 419 | iframeFrames.splice(i, 1); 420 | iframeWindows.splice(i, 1); 421 | } 422 | } 423 | } 424 | export function linkFrameWindow(frame) { 425 | cleanIframes(); 426 | if (frame && frame.contentWindow) { 427 | try { 428 | iframeWindows.push(frame.contentWindow); 429 | iframeFrames.push(frame); 430 | } catch (err) {} 431 | } 432 | } 433 | export function getUserAgent(win) { 434 | win = win || window; 435 | return win.navigator.mockUserAgent || win.navigator.userAgent; 436 | } 437 | export function getFrameByName(win, name) { 438 | var winFrames = getFrames(win); 439 | for (var _i9 = 0; _i9 < winFrames.length; _i9++) { 440 | var childFrame = winFrames[_i9]; 441 | try { 442 | if (isSameDomain(childFrame) && childFrame.name === name && winFrames.indexOf(childFrame) !== -1) { 443 | return childFrame; 444 | } 445 | } catch (err) {} 446 | } 447 | try { 448 | if (winFrames.indexOf(win.frames[name]) !== -1) { 449 | return win.frames[name]; 450 | } 451 | } catch (err) {} 452 | try { 453 | if (winFrames.indexOf(win[name]) !== -1) { 454 | return win[name]; 455 | } 456 | } catch (err) {} 457 | } 458 | export function findChildFrameByName(win, name) { 459 | var frame = getFrameByName(win, name); 460 | if (frame) { 461 | return frame; 462 | } 463 | for (var _i11 = 0, _getFrames4 = getFrames(win); _i11 < _getFrames4.length; _i11++) { 464 | var childFrame = _getFrames4[_i11]; 465 | var namedFrame = findChildFrameByName(childFrame, name); 466 | if (namedFrame) { 467 | return namedFrame; 468 | } 469 | } 470 | } 471 | export function findFrameByName(win, name) { 472 | var frame = getFrameByName(win, name); 473 | if (frame) { 474 | return frame; 475 | } 476 | var top = getTop(win) || win; 477 | return findChildFrameByName(top, name); 478 | } 479 | export function isParent(win, frame) { 480 | var frameParent = getParent(frame); 481 | if (frameParent) { 482 | return frameParent === win; 483 | } 484 | for (var _i13 = 0, _getFrames6 = getFrames(win); _i13 < _getFrames6.length; _i13++) { 485 | var childFrame = _getFrames6[_i13]; 486 | if (childFrame === frame) { 487 | return true; 488 | } 489 | } 490 | return false; 491 | } 492 | export function isOpener(parent, child) { 493 | return parent === getOpener(child); 494 | } 495 | export function getAncestor(win) { 496 | if (win === void 0) { 497 | win = window; 498 | } 499 | win = win || window; 500 | var opener = getOpener(win); 501 | if (opener) { 502 | return opener; 503 | } 504 | var parent = getParent(win); 505 | if (parent) { 506 | return parent; 507 | } 508 | } 509 | export function getAncestors(win) { 510 | var results = []; 511 | var ancestor = win; 512 | while (ancestor) { 513 | ancestor = getAncestor(ancestor); 514 | if (ancestor) { 515 | results.push(ancestor); 516 | } 517 | } 518 | return results; 519 | } 520 | export function isAncestor(parent, child) { 521 | var actualParent = getAncestor(child); 522 | if (actualParent) { 523 | if (actualParent === parent) { 524 | return true; 525 | } 526 | return false; 527 | } 528 | if (child === parent) { 529 | return false; 530 | } 531 | if (getTop(child) === child) { 532 | return false; 533 | } 534 | for (var _i15 = 0, _getFrames8 = getFrames(parent); _i15 < _getFrames8.length; _i15++) { 535 | var frame = _getFrames8[_i15]; 536 | if (frame === child) { 537 | return true; 538 | } 539 | } 540 | return false; 541 | } 542 | export function isPopup(win) { 543 | if (win === void 0) { 544 | win = window; 545 | } 546 | return Boolean(getOpener(win)); 547 | } 548 | export function isIframe(win) { 549 | if (win === void 0) { 550 | win = window; 551 | } 552 | return Boolean(getParent(win)); 553 | } 554 | export function isFullpage(win) { 555 | if (win === void 0) { 556 | win = window; 557 | } 558 | return Boolean(!isIframe(win) && !isPopup(win)); 559 | } 560 | function anyMatch(collection1, collection2) { 561 | for (var _i17 = 0; _i17 < collection1.length; _i17++) { 562 | var item1 = collection1[_i17]; 563 | for (var _i19 = 0; _i19 < collection2.length; _i19++) { 564 | var item2 = collection2[_i19]; 565 | if (item1 === item2) { 566 | return true; 567 | } 568 | } 569 | } 570 | return false; 571 | } 572 | export function getDistanceFromTop(win) { 573 | if (win === void 0) { 574 | win = window; 575 | } 576 | var distance = 0; 577 | var parent = win; 578 | while (parent) { 579 | parent = getParent(parent); 580 | if (parent) { 581 | distance += 1; 582 | } 583 | } 584 | return distance; 585 | } 586 | export function getNthParent(win, n) { 587 | if (n === void 0) { 588 | n = 1; 589 | } 590 | var parent = win; 591 | for (var i = 0; i < n; i++) { 592 | if (!parent) { 593 | return; 594 | } 595 | parent = getParent(parent); 596 | } 597 | return parent; 598 | } 599 | export function getNthParentFromTop(win, n) { 600 | if (n === void 0) { 601 | n = 1; 602 | } 603 | return getNthParent(win, getDistanceFromTop(win) - n); 604 | } 605 | export function isSameTopWindow(win1, win2) { 606 | var top1 = getTop(win1) || win1; 607 | var top2 = getTop(win2) || win2; 608 | try { 609 | if (top1 && top2) { 610 | if (top1 === top2) { 611 | return true; 612 | } 613 | return false; 614 | } 615 | } catch (err) {} 616 | var allFrames1 = getAllFramesInWindow(win1); 617 | var allFrames2 = getAllFramesInWindow(win2); 618 | if (anyMatch(allFrames1, allFrames2)) { 619 | return true; 620 | } 621 | var opener1 = getOpener(top1); 622 | var opener2 = getOpener(top2); 623 | if (opener1 && anyMatch(getAllFramesInWindow(opener1), allFrames2)) { 624 | return false; 625 | } 626 | if (opener2 && anyMatch(getAllFramesInWindow(opener2), allFrames1)) { 627 | return false; 628 | } 629 | return false; 630 | } 631 | export function matchDomain(pattern, origin) { 632 | if (typeof pattern === "string") { 633 | if (typeof origin === "string") { 634 | return pattern === WILDCARD || origin === pattern; 635 | } 636 | if (isRegex(origin)) { 637 | return false; 638 | } 639 | if (Array.isArray(origin)) { 640 | return false; 641 | } 642 | } 643 | if (isRegex(pattern)) { 644 | if (isRegex(origin)) { 645 | return pattern.toString() === origin.toString(); 646 | } 647 | if (Array.isArray(origin)) { 648 | return false; 649 | } 650 | return Boolean(origin.match(pattern)); 651 | } 652 | if (Array.isArray(pattern)) { 653 | if (Array.isArray(origin)) { 654 | return JSON.stringify(pattern) === JSON.stringify(origin); 655 | } 656 | if (isRegex(origin)) { 657 | return false; 658 | } 659 | return pattern.some(function (subpattern) { 660 | return matchDomain(subpattern, origin); 661 | }); 662 | } 663 | return false; 664 | } 665 | export function stringifyDomainPattern(pattern) { 666 | if (Array.isArray(pattern)) { 667 | return "(" + pattern.join(" | ") + ")"; 668 | } else if (isRegex(pattern)) { 669 | return "RegExp(" + pattern.toString() + ")"; 670 | } else { 671 | return pattern.toString(); 672 | } 673 | } 674 | export function getDomainFromUrl(url) { 675 | var domain; 676 | if (url.match(/^(https?|mock|file):\/\//)) { 677 | domain = url; 678 | } else { 679 | return getDomain(); 680 | } 681 | domain = domain.split("/").slice(0, 3).join("/"); 682 | return domain; 683 | } 684 | export function onCloseWindow(win, callback, delay, maxtime) { 685 | if (delay === void 0) { 686 | delay = 1000; 687 | } 688 | if (maxtime === void 0) { 689 | maxtime = Infinity; 690 | } 691 | var timeout; 692 | var check = function check() { 693 | if (isWindowClosed(win)) { 694 | if (timeout) { 695 | clearTimeout(timeout); 696 | } 697 | return callback(); 698 | } 699 | if (maxtime <= 0) { 700 | clearTimeout(timeout); 701 | } else { 702 | maxtime -= delay; 703 | timeout = setTimeout(check, delay); 704 | } 705 | }; 706 | check(); 707 | return { 708 | cancel: function cancel() { 709 | if (timeout) { 710 | clearTimeout(timeout); 711 | } 712 | } 713 | }; 714 | } 715 | export function isWindow(obj) { 716 | try { 717 | if (obj === window) { 718 | return true; 719 | } 720 | } catch (err) { 721 | if (err && err.message === IE_WIN_ACCESS_ERROR) { 722 | return true; 723 | } 724 | } 725 | try { 726 | if (Object.prototype.toString.call(obj) === "[object Window]") { 727 | return true; 728 | } 729 | } catch (err) { 730 | if (err && err.message === IE_WIN_ACCESS_ERROR) { 731 | return true; 732 | } 733 | } 734 | try { 735 | if (window.Window && obj instanceof window.Window) { 736 | return true; 737 | } 738 | } catch (err) { 739 | if (err && err.message === IE_WIN_ACCESS_ERROR) { 740 | return true; 741 | } 742 | } 743 | try { 744 | if (obj && obj.self === obj) { 745 | return true; 746 | } 747 | } catch (err) { 748 | if (err && err.message === IE_WIN_ACCESS_ERROR) { 749 | return true; 750 | } 751 | } 752 | try { 753 | if (obj && obj.parent === obj) { 754 | return true; 755 | } 756 | } catch (err) { 757 | if (err && err.message === IE_WIN_ACCESS_ERROR) { 758 | return true; 759 | } 760 | } 761 | try { 762 | if (obj && obj.top === obj) { 763 | return true; 764 | } 765 | } catch (err) { 766 | if (err && err.message === IE_WIN_ACCESS_ERROR) { 767 | return true; 768 | } 769 | } 770 | try { 771 | if (noop(obj === obj) === "__unlikely_value__") { 772 | return false; 773 | } 774 | } catch (err) { 775 | return true; 776 | } 777 | try { 778 | if (obj && obj.__cross_domain_utils_window_check__ === "__unlikely_value__") { 779 | return false; 780 | } 781 | } catch (err) { 782 | return true; 783 | } 784 | try { 785 | if ("postMessage" in obj && "self" in obj && "location" in obj) { 786 | return true; 787 | } 788 | } catch (err) {} 789 | return false; 790 | } 791 | export function isBrowser() { 792 | return typeof window !== "undefined" && typeof window.location !== "undefined"; 793 | } 794 | export function isCurrentDomain(domain) { 795 | if (!isBrowser()) { 796 | return false; 797 | } 798 | return getDomain() === domain; 799 | } 800 | export function isMockDomain(domain) { 801 | return domain.indexOf(PROTOCOL.MOCK) === 0; 802 | } 803 | export function normalizeMockUrl(url) { 804 | if (!isMockDomain(getDomainFromUrl(url))) { 805 | return url; 806 | } 807 | if (!__TEST__) { 808 | throw new Error("Mock urls not supported out of test mode"); 809 | } 810 | return url.replace(/^mock:\/\/[^/]+/, getActualDomain(window)); 811 | } 812 | export function getFrameForWindow(win) { 813 | if (isSameDomain(win)) { 814 | return assertSameDomain(win).frameElement; 815 | } 816 | for (var _i21 = 0, _document$querySelect2 = document.querySelectorAll("iframe"); _i21 < _document$querySelect2.length; _i21++) { 817 | var frame = _document$querySelect2[_i21]; 818 | if (frame && frame.contentWindow && frame.contentWindow === win) { 819 | return frame; 820 | } 821 | } 822 | } 823 | export function closeWindow(win) { 824 | if (isIframe(win)) { 825 | var frame = getFrameForWindow(win); 826 | if (frame && frame.parentElement) { 827 | frame.parentElement.removeChild(frame); 828 | return; 829 | } 830 | } 831 | try { 832 | win.close(); 833 | } catch (err) {} 834 | } -------------------------------------------------------------------------------- /karma.conf.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { getKarmaConfig } from "@krakenjs/karma-config-grumbler"; 4 | 5 | import { WEBPACK_CONFIG_TEST } from "./webpack.config"; 6 | 7 | export default function configKarma(karma: Object) { 8 | let karmaConfig = getKarmaConfig(karma, { 9 | basePath: __dirname, 10 | webpack: WEBPACK_CONFIG_TEST, 11 | }); 12 | 13 | karma.set(karmaConfig); 14 | } 15 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@krakenjs/cross-domain-utils", 3 | "version": "3.1.0", 4 | "description": "Utilities for dealing with cross-domain windows.", 5 | "main": "dist/cross-domain-utils.js", 6 | "module": "dist/module/index.js", 7 | "scripts": { 8 | "build": "npm run test && npm run babel && npm run webpack", 9 | "webpack": "cross-env NODE_ENV=production babel-node --plugins=transform-es2015-modules-commonjs ./node_modules/.bin/webpack --progress --output-path dist", 10 | "babel": "cross-env NODE_ENV=production babel src/ --out-dir dist/module", 11 | "karma": "cross-env NODE_ENV=test babel-node --plugins=transform-es2015-modules-commonjs ./node_modules/.bin/karma start", 12 | "format": "prettier --write --ignore-unknown .", 13 | "format:check": "prettier --check .", 14 | "test": "npm run format:check && npm run lint && npm run flow-typed && npm run flow && npm run karma", 15 | "lint": "eslint src/ test/", 16 | "flow": "flow", 17 | "flow-typed": "rm -rf ./flow-typed && flow-typed install", 18 | "clean": "rimraf dist coverage", 19 | "reinstall": "rimraf flow-typed && rimraf node_modules && npm install && flow-typed install", 20 | "prerelease": "npm run clean && npm run build && git add dist && git commit -m 'ci: check in dist folder' || echo 'Nothing to distribute'", 21 | "release": "standard-version", 22 | "postrelease": "git push && git push --follow-tags && npm publish", 23 | "debug": "cross-env NODE_ENV=debug", 24 | "prepare": "husky install" 25 | }, 26 | "repository": { 27 | "type": "git", 28 | "url": "git://github.com/krakenjs/cross-domain-utils.git" 29 | }, 30 | "standard-version": { 31 | "types": [ 32 | { 33 | "type": "feat", 34 | "section": "Features" 35 | }, 36 | { 37 | "type": "fix", 38 | "section": "Bug Fixes" 39 | }, 40 | { 41 | "type": "chore", 42 | "hidden": false 43 | }, 44 | { 45 | "type": "docs", 46 | "hidden": false 47 | }, 48 | { 49 | "type": "style", 50 | "hidden": false 51 | }, 52 | { 53 | "type": "refactor", 54 | "hidden": false 55 | }, 56 | { 57 | "type": "perf", 58 | "hidden": false 59 | }, 60 | { 61 | "type": "test", 62 | "hidden": false 63 | }, 64 | { 65 | "type": "ci", 66 | "hidden": true 67 | } 68 | ] 69 | }, 70 | "keywords": [ 71 | "template" 72 | ], 73 | "license": "Apache-2.0", 74 | "files": [ 75 | "src/", 76 | "dist/" 77 | ], 78 | "readmeFilename": "README.md", 79 | "devDependencies": { 80 | "@commitlint/cli": "^16.2.1", 81 | "@commitlint/config-conventional": "^16.2.1", 82 | "@krakenjs/grumbler-scripts": "^8.0.5", 83 | "chai": "^4.2.0", 84 | "cross-env": "^7.0.3", 85 | "flow-bin": "0.155.0", 86 | "flow-typed": "^3.8.0", 87 | "husky": "^7.0.4", 88 | "lint-staged": "^13.0.3", 89 | "mocha": "^4", 90 | "prettier": "2.7.1", 91 | "standard-version": "^9.3.2" 92 | }, 93 | "lint-staged": { 94 | "**/*": "prettier --write --ignore-unknown" 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/constants.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | export const PROTOCOL = { 4 | MOCK: ("mock:": "mock:"), 5 | FILE: ("file:": "file:"), 6 | ABOUT: ("about:": "about:"), 7 | }; 8 | 9 | export const WILDCARD = "*"; 10 | 11 | export const WINDOW_TYPE = { 12 | IFRAME: ("iframe": "iframe"), 13 | POPUP: ("popup": "popup"), 14 | }; 15 | -------------------------------------------------------------------------------- /src/index.flow.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | declare var __TEST__: boolean; 3 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | export * from "./utils"; 4 | export * from "./types"; 5 | export * from "./constants"; 6 | -------------------------------------------------------------------------------- /src/types.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | // export something to force webpack to see this as an ES module 4 | export const TYPES = true; 5 | 6 | export type CrossDomainLocationType = {||}; 7 | 8 | export type CrossDomainWindowType = {| 9 | location: string | CrossDomainLocationType, 10 | self: CrossDomainWindowType, 11 | closed: boolean, 12 | open: (string, string, string) => CrossDomainWindowType, 13 | close: () => void, 14 | focus: () => void, 15 | top: CrossDomainWindowType, 16 | frames: $ReadOnlyArray, 17 | opener?: CrossDomainWindowType, 18 | parent: CrossDomainWindowType, 19 | length: number, 20 | postMessage: (string, string) => void, 21 | |}; 22 | 23 | export type SameDomainWindowType = Object & {| 24 | location: string | Object, 25 | self: CrossDomainWindowType, 26 | closed: boolean, 27 | open: (string, string, string) => CrossDomainWindowType, 28 | close: () => void, 29 | focus: () => void, 30 | XMLHttpRequest: typeof XMLHttpRequest, 31 | document: Document, 32 | navigator: {| 33 | userAgent: string, 34 | mockUserAgent?: string, 35 | |}, 36 | |}; 37 | 38 | export type DomainMatcher = string | $ReadOnlyArray | RegExp; 39 | -------------------------------------------------------------------------------- /src/util.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | export function isRegex(item: mixed): boolean { 4 | // $FlowFixMe method-unbinding 5 | return Object.prototype.toString.call(item) === "[object RegExp]"; 6 | } 7 | 8 | // eslint-disable-next-line no-unused-vars 9 | export function noop(...args: $ReadOnlyArray) { 10 | // pass 11 | } 12 | -------------------------------------------------------------------------------- /src/utils.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | /* eslint max-lines: 0 */ 3 | 4 | import { isRegex, noop } from "./util"; 5 | import type { 6 | CrossDomainWindowType, 7 | SameDomainWindowType, 8 | DomainMatcher, 9 | } from "./types"; 10 | import { PROTOCOL, WILDCARD } from "./constants"; 11 | 12 | const IE_WIN_ACCESS_ERROR = "Call was rejected by callee.\r\n"; 13 | 14 | export function getActualProtocol(win: SameDomainWindowType = window): ?string { 15 | return win.location.protocol; 16 | } 17 | 18 | export function getProtocol(win: SameDomainWindowType = window): ?string { 19 | if (win.mockDomain) { 20 | const protocol = win.mockDomain.split("//")[0]; 21 | 22 | if (protocol) { 23 | return protocol; 24 | } 25 | } 26 | 27 | return getActualProtocol(win); 28 | } 29 | 30 | export function isFileProtocol(win: SameDomainWindowType = window): boolean { 31 | return getProtocol(win) === PROTOCOL.FILE; 32 | } 33 | 34 | export function isAboutProtocol(win: SameDomainWindowType = window): boolean { 35 | return getProtocol(win) === PROTOCOL.ABOUT; 36 | } 37 | 38 | export function isMockProtocol(win: SameDomainWindowType = window): boolean { 39 | return getProtocol(win) === PROTOCOL.MOCK; 40 | } 41 | 42 | export function getParent( 43 | win?: CrossDomainWindowType = window 44 | ): ?CrossDomainWindowType { 45 | if (!win) { 46 | return; 47 | } 48 | 49 | try { 50 | if (win.parent && win.parent !== win) { 51 | return win.parent; 52 | } 53 | } catch (err) { 54 | // pass 55 | } 56 | } 57 | 58 | export function getOpener( 59 | win?: CrossDomainWindowType = window 60 | ): ?CrossDomainWindowType { 61 | if (!win) { 62 | return; 63 | } 64 | 65 | // Make sure we're not actually an iframe which has had window.open() called on us 66 | if (getParent(win)) { 67 | return; 68 | } 69 | 70 | try { 71 | return win.opener; 72 | } catch (err) { 73 | // pass 74 | } 75 | } 76 | 77 | export function canReadFromWindow( 78 | win: CrossDomainWindowType | SameDomainWindowType 79 | ): boolean { 80 | try { 81 | // $FlowFixMe 82 | noop(win && win.location && win.location.href); 83 | return true; 84 | } catch (err) { 85 | // pass 86 | } 87 | 88 | return false; 89 | } 90 | 91 | export function getActualDomain(win?: SameDomainWindowType = window): string { 92 | const location = win.location; 93 | 94 | if (!location) { 95 | throw new Error(`Can not read window location`); 96 | } 97 | 98 | const protocol = getActualProtocol(win); 99 | 100 | if (!protocol) { 101 | throw new Error(`Can not read window protocol`); 102 | } 103 | 104 | if (protocol === PROTOCOL.FILE) { 105 | return `${PROTOCOL.FILE}//`; 106 | } 107 | 108 | if (protocol === PROTOCOL.ABOUT) { 109 | const parent = getParent(win); 110 | if (parent && canReadFromWindow(parent)) { 111 | // $FlowFixMe 112 | return getActualDomain(parent); 113 | } 114 | 115 | return `${PROTOCOL.ABOUT}//`; 116 | } 117 | 118 | const host = location.host; 119 | 120 | if (!host) { 121 | throw new Error(`Can not read window host`); 122 | } 123 | 124 | return `${protocol}//${host}`; 125 | } 126 | 127 | export function getDomain(win?: SameDomainWindowType = window): string { 128 | const domain = getActualDomain(win); 129 | 130 | if (domain && win.mockDomain && win.mockDomain.indexOf(PROTOCOL.MOCK) === 0) { 131 | return win.mockDomain; 132 | } 133 | 134 | return domain; 135 | } 136 | 137 | export function isBlankDomain(win: CrossDomainWindowType): boolean { 138 | try { 139 | // $FlowFixMe 140 | if (!win.location.href) { 141 | return true; 142 | } 143 | 144 | if (win.location.href === "about:blank") { 145 | return true; 146 | } 147 | } catch (err) { 148 | // pass 149 | } 150 | 151 | return false; 152 | } 153 | 154 | export function isActuallySameDomain(win: CrossDomainWindowType): boolean { 155 | try { 156 | if (win === window) { 157 | return true; 158 | } 159 | } catch (err) { 160 | // pass 161 | } 162 | 163 | try { 164 | const desc = Object.getOwnPropertyDescriptor(win, "location"); 165 | 166 | if (desc && desc.enumerable === false) { 167 | return false; 168 | } 169 | } catch (err) { 170 | // pass 171 | } 172 | 173 | try { 174 | // $FlowFixMe 175 | if (isAboutProtocol(win) && canReadFromWindow(win)) { 176 | return true; 177 | } 178 | } catch (err) { 179 | // pass 180 | } 181 | 182 | try { 183 | // $FlowFixMe 184 | if (isMockProtocol(win) && canReadFromWindow(win)) { 185 | return true; 186 | } 187 | } catch (err) { 188 | // pass 189 | } 190 | 191 | try { 192 | // $FlowFixMe 193 | if (getActualDomain(win) === getActualDomain(window)) { 194 | return true; 195 | } 196 | } catch (err) { 197 | // pass 198 | } 199 | 200 | return false; 201 | } 202 | 203 | export function isSameDomain( 204 | win: CrossDomainWindowType | SameDomainWindowType 205 | ): boolean { 206 | if (!isActuallySameDomain(win)) { 207 | return false; 208 | } 209 | 210 | try { 211 | if (win === window) { 212 | return true; 213 | } 214 | 215 | // $FlowFixMe 216 | if (isAboutProtocol(win) && canReadFromWindow(win)) { 217 | return true; 218 | } 219 | 220 | // $FlowFixMe 221 | if (getDomain(window) === getDomain(win)) { 222 | return true; 223 | } 224 | } catch (err) { 225 | // pass 226 | } 227 | 228 | return false; 229 | } 230 | 231 | export function assertSameDomain( 232 | win: CrossDomainWindowType | SameDomainWindowType 233 | ): SameDomainWindowType { 234 | if (!isSameDomain(win)) { 235 | throw new Error(`Expected window to be same domain`); 236 | } 237 | 238 | // $FlowFixMe 239 | return win; 240 | } 241 | 242 | export function getParents( 243 | win: CrossDomainWindowType 244 | ): $ReadOnlyArray { 245 | const result = []; 246 | 247 | try { 248 | while (win.parent !== win) { 249 | result.push(win.parent); 250 | win = win.parent; 251 | } 252 | } catch (err) { 253 | // pass 254 | } 255 | 256 | return result; 257 | } 258 | 259 | export function isAncestorParent( 260 | parent: CrossDomainWindowType, 261 | child: CrossDomainWindowType 262 | ): boolean { 263 | if (!parent || !child) { 264 | return false; 265 | } 266 | 267 | const childParent = getParent(child); 268 | 269 | if (childParent) { 270 | return childParent === parent; 271 | } 272 | 273 | if (getParents(child).indexOf(parent) !== -1) { 274 | return true; 275 | } 276 | 277 | return false; 278 | } 279 | 280 | export function getFrames( 281 | win: CrossDomainWindowType 282 | ): $ReadOnlyArray { 283 | const result = []; 284 | 285 | let frames; 286 | 287 | try { 288 | frames = win.frames; 289 | } catch (err) { 290 | frames = win; 291 | } 292 | 293 | let len; 294 | 295 | try { 296 | len = frames.length; 297 | } catch (err) { 298 | // pass 299 | } 300 | 301 | if (len === 0) { 302 | return result; 303 | } 304 | 305 | if (len) { 306 | for (let i = 0; i < len; i++) { 307 | let frame; 308 | 309 | try { 310 | frame = frames[i]; 311 | } catch (err) { 312 | continue; 313 | } 314 | 315 | result.push(frame); 316 | } 317 | 318 | return result; 319 | } 320 | 321 | for (let i = 0; i < 100; i++) { 322 | let frame; 323 | 324 | try { 325 | frame = frames[i]; 326 | } catch (err) { 327 | return result; 328 | } 329 | 330 | if (!frame) { 331 | return result; 332 | } 333 | 334 | result.push(frame); 335 | } 336 | 337 | return result; 338 | } 339 | 340 | export function getAllChildFrames( 341 | win: CrossDomainWindowType 342 | ): $ReadOnlyArray { 343 | const result = []; 344 | 345 | for (const frame of getFrames(win)) { 346 | result.push(frame); 347 | 348 | for (const childFrame of getAllChildFrames(frame)) { 349 | result.push(childFrame); 350 | } 351 | } 352 | 353 | return result; 354 | } 355 | 356 | export function getTop( 357 | win?: CrossDomainWindowType = window 358 | ): ?CrossDomainWindowType { 359 | try { 360 | if (win.top) { 361 | return win.top; 362 | } 363 | } catch (err) { 364 | // pass 365 | } 366 | 367 | if (getParent(win) === win) { 368 | return win; 369 | } 370 | 371 | try { 372 | if (isAncestorParent(window, win) && window.top) { 373 | return window.top; 374 | } 375 | } catch (err) { 376 | // pass 377 | } 378 | 379 | try { 380 | if (isAncestorParent(win, window) && window.top) { 381 | return window.top; 382 | } 383 | } catch (err) { 384 | // pass 385 | } 386 | 387 | for (const frame of getAllChildFrames(win)) { 388 | try { 389 | if (frame.top) { 390 | return frame.top; 391 | } 392 | } catch (err) { 393 | // pass 394 | } 395 | 396 | if (getParent(frame) === frame) { 397 | return frame; 398 | } 399 | } 400 | } 401 | 402 | export function getNextOpener( 403 | win?: CrossDomainWindowType = window 404 | ): ?CrossDomainWindowType { 405 | return getOpener(getTop(win) || win); 406 | } 407 | 408 | export function getUltimateTop( 409 | win?: CrossDomainWindowType = window 410 | ): CrossDomainWindowType { 411 | const opener = getNextOpener(win); 412 | 413 | if (opener) { 414 | return getUltimateTop(opener); 415 | } 416 | 417 | return top; 418 | } 419 | 420 | export function getAllFramesInWindow( 421 | win: CrossDomainWindowType 422 | ): $ReadOnlyArray { 423 | const top = getTop(win); 424 | 425 | if (!top) { 426 | throw new Error(`Can not determine top window`); 427 | } 428 | 429 | let result = [...getAllChildFrames(top), top]; 430 | 431 | // Win may be in shadow dom 432 | if (result.indexOf(win) === -1) { 433 | result = [...result, win, ...getAllChildFrames(win)]; 434 | } 435 | 436 | return result; 437 | } 438 | 439 | export function getAllWindows( 440 | win?: CrossDomainWindowType = window 441 | ): $ReadOnlyArray { 442 | const frames = getAllFramesInWindow(win); 443 | const opener = getNextOpener(win); 444 | 445 | if (opener) { 446 | return [...getAllWindows(opener), ...frames]; 447 | } else { 448 | return frames; 449 | } 450 | } 451 | 452 | export function isTop(win: CrossDomainWindowType): boolean { 453 | return win === getTop(win); 454 | } 455 | 456 | export function isFrameWindowClosed(frame: HTMLIFrameElement): boolean { 457 | if (!frame.contentWindow) { 458 | return true; 459 | } 460 | 461 | if (!frame.parentNode) { 462 | return true; 463 | } 464 | 465 | const doc = frame.ownerDocument; 466 | 467 | if (doc && doc.documentElement && !doc.documentElement.contains(frame)) { 468 | let parent = frame; 469 | 470 | while (parent.parentNode && parent.parentNode !== parent) { 471 | parent = parent.parentNode; 472 | } 473 | 474 | // $FlowFixMe 475 | if (!parent.host || !doc.documentElement.contains(parent.host)) { 476 | return true; 477 | } 478 | } 479 | 480 | return false; 481 | } 482 | 483 | function safeIndexOf(collection: $ReadOnlyArray, item: T): number { 484 | for (let i = 0; i < collection.length; i++) { 485 | try { 486 | if (collection[i] === item) { 487 | return i; 488 | } 489 | } catch (err) { 490 | // pass 491 | } 492 | } 493 | 494 | return -1; 495 | } 496 | 497 | const iframeWindows = []; 498 | const iframeFrames = []; 499 | 500 | export function isWindowClosed( 501 | win: CrossDomainWindowType, 502 | allowMock: boolean = true 503 | ): boolean { 504 | try { 505 | if (win === window) { 506 | return false; 507 | } 508 | } catch (err) { 509 | return true; 510 | } 511 | 512 | try { 513 | if (!win) { 514 | return true; 515 | } 516 | } catch (err) { 517 | return true; 518 | } 519 | 520 | try { 521 | if (win.closed) { 522 | return true; 523 | } 524 | } catch (err) { 525 | // I love you so much IE 526 | 527 | if (err && err.message === IE_WIN_ACCESS_ERROR) { 528 | return false; 529 | } 530 | 531 | return true; 532 | } 533 | 534 | if (allowMock && isSameDomain(win)) { 535 | try { 536 | // $FlowFixMe 537 | if (win.mockclosed) { 538 | return true; 539 | } 540 | } catch (err) { 541 | // pass 542 | } 543 | } 544 | 545 | // Mobile safari 546 | 547 | try { 548 | if (!win.parent || !win.top) { 549 | return true; 550 | } 551 | } catch (err) { 552 | // pass 553 | } 554 | 555 | // Yes, this actually happens in IE. win === win errors out when the window 556 | // is from an iframe, and the iframe was removed from the page. 557 | 558 | try { 559 | noop(win === win); // eslint-disable-line no-self-compare 560 | } catch (err) { 561 | return true; 562 | } 563 | 564 | // IE orphaned frame 565 | 566 | const iframeIndex = safeIndexOf(iframeWindows, win); 567 | 568 | if (iframeIndex !== -1) { 569 | const frame = iframeFrames[iframeIndex]; 570 | 571 | if (frame && isFrameWindowClosed(frame)) { 572 | return true; 573 | } 574 | } 575 | 576 | return false; 577 | } 578 | 579 | function cleanIframes() { 580 | for (let i = 0; i < iframeWindows.length; i++) { 581 | let closed = false; 582 | 583 | try { 584 | closed = iframeWindows[i].closed; 585 | } catch (err) { 586 | // pass 587 | } 588 | 589 | if (closed) { 590 | iframeFrames.splice(i, 1); 591 | iframeWindows.splice(i, 1); 592 | } 593 | } 594 | } 595 | 596 | export function linkFrameWindow(frame: HTMLIFrameElement) { 597 | cleanIframes(); 598 | 599 | if (frame && frame.contentWindow) { 600 | try { 601 | iframeWindows.push(frame.contentWindow); 602 | iframeFrames.push(frame); 603 | } catch (err) { 604 | // pass 605 | } 606 | } 607 | } 608 | 609 | export function getUserAgent(win: ?SameDomainWindowType): string { 610 | win = win || window; 611 | return win.navigator.mockUserAgent || win.navigator.userAgent; 612 | } 613 | 614 | export function getFrameByName( 615 | win: CrossDomainWindowType, 616 | name: string 617 | ): ?CrossDomainWindowType { 618 | const winFrames = getFrames(win); 619 | 620 | for (const childFrame of winFrames) { 621 | try { 622 | if ( 623 | isSameDomain(childFrame) && 624 | // $FlowFixMe 625 | childFrame.name === name && 626 | winFrames.indexOf(childFrame) !== -1 627 | ) { 628 | return childFrame; 629 | } 630 | } catch (err) { 631 | // pass 632 | } 633 | } 634 | 635 | try { 636 | // $FlowFixMe 637 | if (winFrames.indexOf(win.frames[name]) !== -1) { 638 | // $FlowFixMe 639 | return win.frames[name]; 640 | } 641 | } catch (err) { 642 | // pass 643 | } 644 | 645 | try { 646 | if (winFrames.indexOf(win[name]) !== -1) { 647 | return win[name]; 648 | } 649 | } catch (err) { 650 | // pass 651 | } 652 | } 653 | 654 | export function findChildFrameByName( 655 | win: CrossDomainWindowType, 656 | name: string 657 | ): ?CrossDomainWindowType { 658 | const frame = getFrameByName(win, name); 659 | 660 | if (frame) { 661 | return frame; 662 | } 663 | 664 | for (const childFrame of getFrames(win)) { 665 | const namedFrame = findChildFrameByName(childFrame, name); 666 | 667 | if (namedFrame) { 668 | return namedFrame; 669 | } 670 | } 671 | } 672 | 673 | export function findFrameByName( 674 | win: CrossDomainWindowType, 675 | name: string 676 | ): ?CrossDomainWindowType { 677 | const frame = getFrameByName(win, name); 678 | 679 | if (frame) { 680 | return frame; 681 | } 682 | 683 | const top = getTop(win) || win; 684 | 685 | return findChildFrameByName(top, name); 686 | } 687 | 688 | export function isParent( 689 | win: CrossDomainWindowType, 690 | frame: CrossDomainWindowType 691 | ): boolean { 692 | const frameParent = getParent(frame); 693 | 694 | if (frameParent) { 695 | return frameParent === win; 696 | } 697 | 698 | for (const childFrame of getFrames(win)) { 699 | if (childFrame === frame) { 700 | return true; 701 | } 702 | } 703 | 704 | return false; 705 | } 706 | 707 | export function isOpener( 708 | parent: CrossDomainWindowType, 709 | child: CrossDomainWindowType 710 | ): boolean { 711 | return parent === getOpener(child); 712 | } 713 | 714 | export function getAncestor( 715 | win?: CrossDomainWindowType = window 716 | ): ?CrossDomainWindowType { 717 | win = win || window; 718 | 719 | const opener = getOpener(win); 720 | 721 | if (opener) { 722 | return opener; 723 | } 724 | 725 | const parent = getParent(win); 726 | 727 | if (parent) { 728 | return parent; 729 | } 730 | } 731 | 732 | export function getAncestors( 733 | win: CrossDomainWindowType 734 | ): $ReadOnlyArray { 735 | const results = []; 736 | 737 | let ancestor = win; 738 | 739 | while (ancestor) { 740 | ancestor = getAncestor(ancestor); 741 | if (ancestor) { 742 | results.push(ancestor); 743 | } 744 | } 745 | 746 | return results; 747 | } 748 | 749 | export function isAncestor( 750 | parent: CrossDomainWindowType, 751 | child: CrossDomainWindowType 752 | ): boolean { 753 | const actualParent = getAncestor(child); 754 | 755 | if (actualParent) { 756 | if (actualParent === parent) { 757 | return true; 758 | } 759 | 760 | return false; 761 | } 762 | 763 | if (child === parent) { 764 | return false; 765 | } 766 | 767 | if (getTop(child) === child) { 768 | return false; 769 | } 770 | 771 | for (const frame of getFrames(parent)) { 772 | if (frame === child) { 773 | return true; 774 | } 775 | } 776 | 777 | return false; 778 | } 779 | 780 | export function isPopup(win?: CrossDomainWindowType = window): boolean { 781 | return Boolean(getOpener(win)); 782 | } 783 | 784 | export function isIframe(win?: CrossDomainWindowType = window): boolean { 785 | return Boolean(getParent(win)); 786 | } 787 | 788 | export function isFullpage(win?: CrossDomainWindowType = window): boolean { 789 | return Boolean(!isIframe(win) && !isPopup(win)); 790 | } 791 | 792 | function anyMatch(collection1, collection2): boolean { 793 | for (const item1 of collection1) { 794 | for (const item2 of collection2) { 795 | if (item1 === item2) { 796 | return true; 797 | } 798 | } 799 | } 800 | 801 | return false; 802 | } 803 | 804 | export function getDistanceFromTop( 805 | win: CrossDomainWindowType = window 806 | ): number { 807 | let distance = 0; 808 | let parent = win; 809 | 810 | while (parent) { 811 | parent = getParent(parent); 812 | if (parent) { 813 | distance += 1; 814 | } 815 | } 816 | 817 | return distance; 818 | } 819 | 820 | export function getNthParent( 821 | win: CrossDomainWindowType, 822 | n: number = 1 823 | ): ?CrossDomainWindowType { 824 | let parent = win; 825 | 826 | for (let i = 0; i < n; i++) { 827 | if (!parent) { 828 | return; 829 | } 830 | 831 | parent = getParent(parent); 832 | } 833 | 834 | return parent; 835 | } 836 | 837 | export function getNthParentFromTop( 838 | win: CrossDomainWindowType, 839 | n: number = 1 840 | ): ?CrossDomainWindowType { 841 | return getNthParent(win, getDistanceFromTop(win) - n); 842 | } 843 | 844 | export function isSameTopWindow( 845 | win1: CrossDomainWindowType, 846 | win2: CrossDomainWindowType 847 | ): boolean { 848 | const top1 = getTop(win1) || win1; 849 | const top2 = getTop(win2) || win2; 850 | 851 | try { 852 | if (top1 && top2) { 853 | if (top1 === top2) { 854 | return true; 855 | } 856 | 857 | return false; 858 | } 859 | } catch (err) { 860 | // pass 861 | } 862 | 863 | const allFrames1 = getAllFramesInWindow(win1); 864 | const allFrames2 = getAllFramesInWindow(win2); 865 | 866 | if (anyMatch(allFrames1, allFrames2)) { 867 | return true; 868 | } 869 | 870 | const opener1 = getOpener(top1); 871 | const opener2 = getOpener(top2); 872 | 873 | if (opener1 && anyMatch(getAllFramesInWindow(opener1), allFrames2)) { 874 | return false; 875 | } 876 | 877 | if (opener2 && anyMatch(getAllFramesInWindow(opener2), allFrames1)) { 878 | return false; 879 | } 880 | 881 | return false; 882 | } 883 | 884 | export function matchDomain( 885 | pattern: DomainMatcher, 886 | origin: DomainMatcher 887 | ): boolean { 888 | if (typeof pattern === "string") { 889 | if (typeof origin === "string") { 890 | return pattern === WILDCARD || origin === pattern; 891 | } 892 | 893 | if (isRegex(origin)) { 894 | return false; 895 | } 896 | 897 | if (Array.isArray(origin)) { 898 | return false; 899 | } 900 | } 901 | 902 | if (isRegex(pattern)) { 903 | if (isRegex(origin)) { 904 | return pattern.toString() === origin.toString(); 905 | } 906 | 907 | if (Array.isArray(origin)) { 908 | return false; 909 | } 910 | 911 | // $FlowFixMe 912 | return Boolean(origin.match(pattern)); 913 | } 914 | 915 | if (Array.isArray(pattern)) { 916 | if (Array.isArray(origin)) { 917 | return JSON.stringify(pattern) === JSON.stringify(origin); 918 | } 919 | 920 | if (isRegex(origin)) { 921 | return false; 922 | } 923 | 924 | return pattern.some((subpattern) => matchDomain(subpattern, origin)); 925 | } 926 | 927 | return false; 928 | } 929 | 930 | export function stringifyDomainPattern(pattern: DomainMatcher): string { 931 | if (Array.isArray(pattern)) { 932 | return `(${pattern.join(" | ")})`; 933 | } else if (isRegex(pattern)) { 934 | return `RegExp(${pattern.toString()})`; 935 | } else { 936 | return pattern.toString(); 937 | } 938 | } 939 | 940 | export function getDomainFromUrl(url: string): string { 941 | let domain; 942 | 943 | if (url.match(/^(https?|mock|file):\/\//)) { 944 | domain = url; 945 | } else { 946 | return getDomain(); 947 | } 948 | 949 | domain = domain.split("/").slice(0, 3).join("/"); 950 | 951 | return domain; 952 | } 953 | 954 | export function onCloseWindow( 955 | win: CrossDomainWindowType, 956 | callback: Function, 957 | delay: number = 1000, 958 | maxtime: number = Infinity 959 | ): {| cancel: () => void |} { 960 | let timeout; 961 | 962 | const check = () => { 963 | if (isWindowClosed(win)) { 964 | if (timeout) { 965 | clearTimeout(timeout); 966 | } 967 | 968 | return callback(); 969 | } 970 | 971 | if (maxtime <= 0) { 972 | clearTimeout(timeout); 973 | } else { 974 | maxtime -= delay; 975 | timeout = setTimeout(check, delay); 976 | } 977 | }; 978 | 979 | check(); 980 | 981 | return { 982 | cancel() { 983 | if (timeout) { 984 | clearTimeout(timeout); 985 | } 986 | }, 987 | }; 988 | } 989 | 990 | // eslint-disable-next-line complexity 991 | export function isWindow(obj: Object): boolean { 992 | try { 993 | if (obj === window) { 994 | return true; 995 | } 996 | } catch (err) { 997 | if (err && err.message === IE_WIN_ACCESS_ERROR) { 998 | return true; 999 | } 1000 | } 1001 | 1002 | try { 1003 | // $FlowFixMe method-unbinding 1004 | if (Object.prototype.toString.call(obj) === "[object Window]") { 1005 | return true; 1006 | } 1007 | } catch (err) { 1008 | if (err && err.message === IE_WIN_ACCESS_ERROR) { 1009 | return true; 1010 | } 1011 | } 1012 | 1013 | try { 1014 | if (window.Window && obj instanceof window.Window) { 1015 | return true; 1016 | } 1017 | } catch (err) { 1018 | if (err && err.message === IE_WIN_ACCESS_ERROR) { 1019 | return true; 1020 | } 1021 | } 1022 | 1023 | try { 1024 | if (obj && obj.self === obj) { 1025 | return true; 1026 | } 1027 | } catch (err) { 1028 | if (err && err.message === IE_WIN_ACCESS_ERROR) { 1029 | return true; 1030 | } 1031 | } 1032 | 1033 | try { 1034 | if (obj && obj.parent === obj) { 1035 | return true; 1036 | } 1037 | } catch (err) { 1038 | if (err && err.message === IE_WIN_ACCESS_ERROR) { 1039 | return true; 1040 | } 1041 | } 1042 | 1043 | try { 1044 | if (obj && obj.top === obj) { 1045 | return true; 1046 | } 1047 | } catch (err) { 1048 | if (err && err.message === IE_WIN_ACCESS_ERROR) { 1049 | return true; 1050 | } 1051 | } 1052 | 1053 | try { 1054 | // eslint-disable-next-line no-self-compare 1055 | if (noop(obj === obj) === "__unlikely_value__") { 1056 | return false; 1057 | } 1058 | } catch (err) { 1059 | return true; 1060 | } 1061 | 1062 | try { 1063 | if ( 1064 | obj && 1065 | obj.__cross_domain_utils_window_check__ === "__unlikely_value__" 1066 | ) { 1067 | return false; 1068 | } 1069 | } catch (err) { 1070 | return true; 1071 | } 1072 | 1073 | try { 1074 | if ("postMessage" in obj && "self" in obj && "location" in obj) { 1075 | return true; 1076 | } 1077 | } catch (err) { 1078 | // pass 1079 | } 1080 | 1081 | return false; 1082 | } 1083 | 1084 | export function isBrowser(): boolean { 1085 | return ( 1086 | typeof window !== "undefined" && typeof window.location !== "undefined" 1087 | ); 1088 | } 1089 | 1090 | export function isCurrentDomain(domain: string): boolean { 1091 | if (!isBrowser()) { 1092 | return false; 1093 | } 1094 | 1095 | return getDomain() === domain; 1096 | } 1097 | 1098 | export function isMockDomain(domain: string): boolean { 1099 | return domain.indexOf(PROTOCOL.MOCK) === 0; 1100 | } 1101 | 1102 | export function normalizeMockUrl(url: string): string { 1103 | if (!isMockDomain(getDomainFromUrl(url))) { 1104 | return url; 1105 | } 1106 | 1107 | if (!__TEST__) { 1108 | throw new Error(`Mock urls not supported out of test mode`); 1109 | } 1110 | 1111 | return url.replace(/^mock:\/\/[^/]+/, getActualDomain(window)); 1112 | } 1113 | 1114 | export function getFrameForWindow(win: CrossDomainWindowType): ?HTMLElement { 1115 | if (isSameDomain(win)) { 1116 | return assertSameDomain(win).frameElement; 1117 | } 1118 | 1119 | for (const frame of document.querySelectorAll("iframe")) { 1120 | if (frame && frame.contentWindow && frame.contentWindow === win) { 1121 | return frame; 1122 | } 1123 | } 1124 | } 1125 | 1126 | export function closeWindow(win: CrossDomainWindowType) { 1127 | if (isIframe(win)) { 1128 | const frame = getFrameForWindow(win); 1129 | if (frame && frame.parentElement) { 1130 | frame.parentElement.removeChild(frame); 1131 | return; 1132 | } 1133 | } 1134 | 1135 | try { 1136 | win.close(); 1137 | } catch (err) { 1138 | // pass 1139 | } 1140 | } 1141 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import "./util"; 4 | 5 | import "./tests"; 6 | -------------------------------------------------------------------------------- /test/tests/closeWindow.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { assert } from "chai"; 4 | 5 | import { closeWindow } from "../../src"; 6 | import { getSameDomainWindow } from "../win"; 7 | 8 | describe("closeWindow", () => { 9 | it("will call window.close", () => { 10 | let fnCalled = false; 11 | 12 | const win = getSameDomainWindow({ 13 | close: () => { 14 | fnCalled = true; 15 | }, 16 | }); 17 | const expectedResult = true; 18 | closeWindow(win); 19 | 20 | assert(fnCalled === expectedResult, `Expected window.close to be called`); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /test/tests/getActualDomain.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { getActualDomain } from "../../src"; 4 | import { getSameDomainWindow } from "../win"; 5 | 6 | describe("getActualDomain cases", () => { 7 | it("should get the domain for a specific window", () => { 8 | const win = getSameDomainWindow({ 9 | location: { 10 | protocol: "https:", 11 | host: "foo.com:8087", 12 | }, 13 | }); 14 | 15 | const domain = getActualDomain(win); 16 | const expectedDomain = `${win.location.protocol}//${win.location.host}`; 17 | 18 | if (domain !== expectedDomain) { 19 | throw new Error( 20 | `Expected domain to be "${expectedDomain}", got "${domain}"` 21 | ); 22 | } 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /test/tests/getAllFramesInWindow.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { getAllFramesInWindow } from "../../src"; 4 | 5 | describe("getAllFramesInWindow cases", () => { 6 | it("should get all of the frames", () => { 7 | const x: Object = { name: "x" }; 8 | const y: Object = { name: "y", frames: [x] }; 9 | 10 | const a: Object = { name: "a" }; 11 | const b: Object = { name: "b", frames: [a] }; 12 | 13 | const z: Object = { name: "z", frames: [b, y] }; 14 | 15 | x.top = z; 16 | x.parent = y; 17 | 18 | y.top = z; 19 | y.parent = z; 20 | 21 | a.top = z; 22 | a.parent = b; 23 | 24 | b.top = z; 25 | b.parent = z; 26 | 27 | z.top = z; 28 | z.parent = z; 29 | 30 | const allFrames = [a, b, x, y, z]; 31 | const foundFrames = getAllFramesInWindow(x); 32 | 33 | if (foundFrames.length !== allFrames.length) { 34 | throw new Error( 35 | `Expected to find ${allFrames.length}, but found ${foundFrames.length}` 36 | ); 37 | } 38 | 39 | for (const frame of allFrames) { 40 | if (foundFrames.indexOf(frame) === -1) { 41 | throw new Error(`Did not find frame ${frame.name}`); 42 | } 43 | } 44 | }); 45 | 46 | it("should get a mock frame defined in window.frames", () => { 47 | const frames = window.frames; 48 | 49 | const mockFrame = {}; 50 | 51 | window.frames = [mockFrame]; 52 | 53 | const foundFrames = getAllFramesInWindow(window); 54 | 55 | if (foundFrames.indexOf(mockFrame) === -1) { 56 | throw new Error( 57 | `getAllFramesInWindow expected to find mock frame in window.frames` 58 | ); 59 | } 60 | 61 | window.frames = frames; 62 | }); 63 | }); 64 | -------------------------------------------------------------------------------- /test/tests/getDomain.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { getDomain } from "../../src"; 4 | import { getSameDomainWindow } from "../win"; 5 | 6 | describe("getDomain cases", () => { 7 | it("should get the domain for the current window", () => { 8 | const domain = getDomain(); 9 | const expectedDomain = `${window.location.protocol}//${window.location.host}`; 10 | 11 | if (domain !== expectedDomain) { 12 | throw new Error( 13 | `Expected domain to be "${expectedDomain}", got "${domain}"` 14 | ); 15 | } 16 | }); 17 | 18 | it("should get the domain for a specific window", () => { 19 | const win = getSameDomainWindow({ 20 | location: { 21 | protocol: "https:", 22 | host: "foo.com:8087", 23 | }, 24 | }); 25 | 26 | const domain = getDomain(win); 27 | const expectedDomain = `${win.location.protocol}//${win.location.host}`; 28 | 29 | if (domain !== expectedDomain) { 30 | throw new Error( 31 | `Expected domain to be "${expectedDomain}", got "${domain}"` 32 | ); 33 | } 34 | }); 35 | 36 | it("should get the mock domain for a specific window", () => { 37 | const win = getSameDomainWindow({ 38 | location: { 39 | protocol: "https:", 40 | host: "foo.com:8087", 41 | }, 42 | mockDomain: "mock://zomg.com:3456", 43 | }); 44 | 45 | const domain = getDomain(win); 46 | const expectedDomain = "mock://zomg.com:3456"; 47 | 48 | if (domain !== expectedDomain) { 49 | throw new Error( 50 | `Expected domain to be "${expectedDomain}", got "${domain}"` 51 | ); 52 | } 53 | }); 54 | 55 | it("should get the actual domain for a specific window when mock domain is not mock://", () => { 56 | const win = getSameDomainWindow({ 57 | location: { 58 | protocol: "https:", 59 | host: "foo.com:8087", 60 | }, 61 | mockDomain: "mocc://zomg.com:3456", 62 | }); 63 | 64 | const domain = getDomain(win); 65 | const expectedDomain = `${win.location.protocol}//${win.location.host}`; 66 | 67 | if (domain !== expectedDomain) { 68 | throw new Error( 69 | `Expected domain to be "${expectedDomain}", got "${domain}"` 70 | ); 71 | } 72 | }); 73 | 74 | it("should throw errors when the window does not have a location", () => { 75 | const win = getSameDomainWindow({ 76 | location: null, 77 | mockDomain: "mocc://zomg.com:3456", 78 | }); 79 | 80 | let error; 81 | 82 | try { 83 | getDomain(win); 84 | } catch (err) { 85 | error = err; 86 | } 87 | 88 | if (!(error instanceof Error)) { 89 | throw new TypeError(`Expected to get Error, got ${typeof error}`); 90 | } 91 | }); 92 | 93 | it("should throw errors when the window does not have a protocol", () => { 94 | const win = getSameDomainWindow({ 95 | location: { 96 | protocol: null, 97 | host: "foo.com:8087", 98 | }, 99 | mockDomain: "mocc://zomg.com:3456", 100 | }); 101 | 102 | let error; 103 | 104 | try { 105 | getDomain(win); 106 | } catch (err) { 107 | error = err; 108 | } 109 | 110 | if (!(error instanceof Error)) { 111 | throw new TypeError(`Expected to get Error, got ${typeof error}`); 112 | } 113 | }); 114 | 115 | it("should throw errors when the window does not have a host", () => { 116 | const win = getSameDomainWindow({ 117 | location: { 118 | protocol: "https:", 119 | host: null, 120 | }, 121 | mockDomain: "mocc://zomg.com:3456", 122 | }); 123 | 124 | let error; 125 | 126 | try { 127 | getDomain(win); 128 | } catch (err) { 129 | error = err; 130 | } 131 | 132 | if (!(error instanceof Error)) { 133 | throw new TypeError(`Expected to get Error, got ${typeof error}`); 134 | } 135 | }); 136 | 137 | it("should get the domain for a specific window when its protocol is file:// even with no host", () => { 138 | const win = getSameDomainWindow({ 139 | location: { 140 | protocol: "file:", 141 | }, 142 | }); 143 | 144 | const domain = getDomain(win); 145 | const expectedDomain = `${win.location.protocol}//`; 146 | 147 | if (domain !== expectedDomain) { 148 | throw new Error( 149 | `Expected domain to be "${expectedDomain}", got "${domain}"` 150 | ); 151 | } 152 | }); 153 | }); 154 | -------------------------------------------------------------------------------- /test/tests/getDomainFromUrl.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { expect } from "chai"; 4 | 5 | import { getDomainFromUrl } from "../../src"; 6 | 7 | describe("getDomainFromUrl test cases ", () => { 8 | it("should get url protocol and domain for a simple http url", () => { 9 | const url = "http://github.com/krakenjs/cross-domain-utils/"; 10 | const expected = "http://github.com"; 11 | 12 | const domain = getDomainFromUrl(url); 13 | expect(domain).to.equal(expected); 14 | }); 15 | 16 | it("should get url protocol and domain for a long https url", () => { 17 | const url = 18 | "https://www.google.com/search?q=Cross+Domain+utilities+npm&oq=cros&aqs=chrome.0.69i59l3j69i57j69i60l3j69i65.1620j0j7&sourceid=chrome&ie=UTF-8"; 19 | const expected = "https://www.google.com"; 20 | 21 | const domain = getDomainFromUrl(url); 22 | 23 | expect(domain).to.equal(expected); 24 | }); 25 | 26 | it("should get url protocol and domain for a url with non-www subdomain", () => { 27 | const url = "https://ca.indeed.com/"; 28 | const expected = "https://ca.indeed.com"; 29 | 30 | const domain = getDomainFromUrl(url); 31 | 32 | expect(domain).to.equal(expected); 33 | }); 34 | 35 | it("should get url protocol and IP of a file url ", () => { 36 | const url = "file://192.168.1.11/~User/TextFile.pdf"; // fake IP 37 | const expected = "file://192.168.1.11"; 38 | 39 | const domain = getDomainFromUrl(url); 40 | 41 | expect(domain).to.equal(expected); 42 | }); 43 | }); 44 | -------------------------------------------------------------------------------- /test/tests/getOpener.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { getOpener } from "../../src"; 4 | import { getCrossDomainWindow } from "../win"; 5 | 6 | describe("getOpener cases", () => { 7 | it("should get the opener window if there is one", () => { 8 | const win = getCrossDomainWindow({ 9 | opener: {}, 10 | }); 11 | 12 | const opener = getOpener(win); 13 | 14 | if (opener !== win.opener) { 15 | throw new Error(`Expected getOpener to return opener window`); 16 | } 17 | }); 18 | 19 | it("should not get the opener window if the window has a parent", () => { 20 | const win = getCrossDomainWindow({ 21 | parent: {}, 22 | opener: {}, 23 | }); 24 | 25 | const opener = getOpener(win); 26 | 27 | if (opener) { 28 | throw new Error(`Expected getOpener to not return a window`); 29 | } 30 | }); 31 | 32 | it("should not get the opener window if nothing is passed", () => { 33 | const opener = getOpener(); 34 | 35 | if (opener) { 36 | throw new Error(`Expected getOpener to not return a window`); 37 | } 38 | }); 39 | 40 | it("should return void in case of any errors", () => { 41 | const win = getCrossDomainWindow({}); 42 | 43 | // $FlowFixMe 44 | Object.defineProperty(win, "opener", { 45 | get() { 46 | throw new Error("error"); 47 | }, 48 | }); 49 | 50 | const opener = getOpener(win); 51 | 52 | if (opener) { 53 | throw new Error(`Expected getOpener to not return a window`); 54 | } 55 | }); 56 | }); 57 | -------------------------------------------------------------------------------- /test/tests/getParent.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { getParent } from "../../src"; 4 | import { getCrossDomainWindow } from "../win"; 5 | 6 | describe("getParent cases", () => { 7 | it("should get the parent window if there is one", () => { 8 | const win = getCrossDomainWindow({ 9 | parent: {}, 10 | }); 11 | 12 | const parent = getParent(win); 13 | 14 | if (parent !== win.parent) { 15 | throw new Error(`Expected getParent to return parent window`); 16 | } 17 | }); 18 | 19 | it("should not get the parent window if the parent is the same window", () => { 20 | const win = getCrossDomainWindow({}); 21 | win.parent = win; 22 | 23 | const parent = getParent(win); 24 | 25 | if (parent) { 26 | throw new Error(`Expected getParent to not return a window`); 27 | } 28 | }); 29 | 30 | it("should return void in case of any errors", () => { 31 | const win = getCrossDomainWindow({}); 32 | 33 | // $FlowFixMe 34 | Object.defineProperty(win, "parent", { 35 | get() { 36 | throw new Error("error"); 37 | }, 38 | }); 39 | 40 | const parent = getParent(win); 41 | 42 | if (parent) { 43 | throw new Error(`Expected getParent to not return a window`); 44 | } 45 | }); 46 | }); 47 | -------------------------------------------------------------------------------- /test/tests/getParents.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { getParents } from "../../src"; 4 | import { getCrossDomainWindow } from "../win"; 5 | 6 | describe("getParents cases", () => { 7 | it("should get all of a windows parents", () => { 8 | const win = getCrossDomainWindow({ 9 | parent: { 10 | parent: { 11 | parent: {}, 12 | }, 13 | }, 14 | }); 15 | 16 | win.parent.parent.parent.parent = win.parent.parent.parent; 17 | 18 | const parents = getParents(win); 19 | 20 | if (parents.length !== 3) { 21 | throw new Error(`Expected to get 3 parents, got ${parents.length}`); 22 | } 23 | 24 | if (parents[0] !== win.parent) { 25 | throw new Error( 26 | `Expected correct parent window to be returned at index 0` 27 | ); 28 | } 29 | 30 | if (parents[1] !== win.parent.parent) { 31 | throw new Error( 32 | `Expected correct parent window to be returned at index 1` 33 | ); 34 | } 35 | 36 | if (parents[2] !== win.parent.parent.parent) { 37 | throw new Error( 38 | `Expected correct parent window to be returned at index 2` 39 | ); 40 | } 41 | }); 42 | }); 43 | -------------------------------------------------------------------------------- /test/tests/getUserAgent.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { getUserAgent } from "../../src"; 4 | import { getSameDomainWindow } from "../win"; 5 | 6 | describe("getUserAgent cases", () => { 7 | it("should get the real user agent", () => { 8 | const userAgent = 9 | "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36"; 10 | 11 | const win = getSameDomainWindow({ 12 | navigator: { 13 | userAgent, 14 | }, 15 | }); 16 | 17 | const result = getUserAgent(win); 18 | 19 | if (result !== userAgent) { 20 | throw new Error( 21 | `Expected getUserAgent to return ${userAgent}, actually got ${result}` 22 | ); 23 | } 24 | }); 25 | 26 | it("should get the mock user agent", () => { 27 | const mockUserAgent = 28 | "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36"; 29 | 30 | const win = getSameDomainWindow({ 31 | navigator: { 32 | mockUserAgent, 33 | }, 34 | }); 35 | 36 | const result = getUserAgent(win); 37 | 38 | if (result !== mockUserAgent) { 39 | throw new Error( 40 | `Expected getUserAgent to return ${mockUserAgent}, actually got ${result}` 41 | ); 42 | } 43 | }); 44 | }); 45 | -------------------------------------------------------------------------------- /test/tests/index.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import "./getActualDomain"; 4 | import "./getDomain"; 5 | import "./isSameDomain"; 6 | import "./matchDomain"; 7 | import "./getParent"; 8 | import "./getOpener"; 9 | import "./getParents"; 10 | import "./getAllFramesInWindow"; 11 | import "./getUserAgent"; 12 | import "./stringifyDomainPattern"; 13 | import "./getDomainFromUrl"; 14 | import "./isBlankDomain"; 15 | import "./closeWindow"; 16 | import "./isFileProtocol"; 17 | -------------------------------------------------------------------------------- /test/tests/isBlankDomain.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { assert } from "chai"; 4 | 5 | import { isBlankDomain } from "../../src"; 6 | import { getSameDomainWindow } from "../win"; 7 | 8 | describe("isBlankDomain", () => { 9 | it("returns true if window.href is falsy", () => { 10 | const falsyValues = [0, "", null, undefined, NaN]; 11 | const windows = falsyValues.map((falsyValue) => 12 | getSameDomainWindow({ 13 | location: { 14 | href: falsyValue, 15 | }, 16 | }) 17 | ); 18 | 19 | const results = windows.map(isBlankDomain); 20 | const expectedResult = true; 21 | 22 | assert( 23 | results.every((result) => result === expectedResult), 24 | `Expected isBlankDomain result to return ${expectedResult.toString()}` 25 | ); 26 | }); 27 | 28 | it("returns true if window.href about:blank", () => { 29 | const win = getSameDomainWindow({ 30 | location: { 31 | href: "about:blank", 32 | }, 33 | }); 34 | const expectedResult = true; 35 | const result = isBlankDomain(win); 36 | 37 | assert( 38 | result === expectedResult, 39 | `Expected isBlankDomain result to be ${expectedResult.toString()}, got ${result.toString()}` 40 | ); 41 | }); 42 | 43 | it("should return false if window.href is truthy but not about:blank", () => { 44 | const win = getSameDomainWindow({ 45 | location: { 46 | href: "someUrl", 47 | }, 48 | }); 49 | const expectedResult = false; 50 | const result = isBlankDomain(win); 51 | 52 | assert( 53 | result === expectedResult, 54 | `Expected isBlankDomain result to be "${expectedResult.toString()}", got "${result.toString()}"` 55 | ); 56 | }); 57 | }); 58 | -------------------------------------------------------------------------------- /test/tests/isFileProtocol.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { assert } from "chai"; 4 | 5 | import { isFileProtocol, PROTOCOL } from "../../src"; 6 | import { getSameDomainWindow } from "../win"; 7 | 8 | describe("isFileProtocol", () => { 9 | it("will return true when window.location is pointing to a file", () => { 10 | const win = getSameDomainWindow({ 11 | location: { 12 | protocol: PROTOCOL.FILE, 13 | }, 14 | }); 15 | const expectedResult = true; 16 | const result = isFileProtocol(win); 17 | 18 | assert( 19 | result === expectedResult, 20 | `Expected result to be ${expectedResult.toString()} but received ${result.toString()}` 21 | ); 22 | }); 23 | 24 | it("will return true when window.location is pointing to a file", () => { 25 | const win = getSameDomainWindow({ 26 | location: { 27 | protocol: PROTOCOL.ABOUT, 28 | }, 29 | }); 30 | const expectedResult = false; 31 | const result = isFileProtocol(win); 32 | 33 | assert( 34 | result === expectedResult, 35 | `Expected result to be ${expectedResult.toString()} but received ${result.toString()}` 36 | ); 37 | }); 38 | }); 39 | -------------------------------------------------------------------------------- /test/tests/isSameDomain.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { isSameDomain } from "../../src"; 4 | import { getSameDomainWindow } from "../win"; 5 | 6 | describe("isSameDomain cases", () => { 7 | it("should give a positive result for isSameDomain", () => { 8 | const win = getSameDomainWindow({ 9 | location: { 10 | protocol: window.location.protocol, 11 | host: window.location.host, 12 | }, 13 | }); 14 | 15 | const result = isSameDomain(win); 16 | const expectedResult = true; 17 | 18 | if (result !== expectedResult) { 19 | throw new Error( 20 | `Expected isSameDomain result to be "${expectedResult.toString()}", got "${result.toString()}"` 21 | ); 22 | } 23 | }); 24 | 25 | it("should give a negative result for isSameDomain with a different protocol", () => { 26 | const win = getSameDomainWindow({ 27 | location: { 28 | protocol: "https:", 29 | host: window.location.host, 30 | }, 31 | }); 32 | 33 | // $FlowFixMe 34 | Object.defineProperty(win.location, "href", { 35 | get(): string { 36 | return `${win.location.protocol}//${win.location.host}`; 37 | }, 38 | }); 39 | 40 | const result = isSameDomain(win); 41 | const expectedResult = false; 42 | 43 | if (result !== expectedResult) { 44 | throw new Error( 45 | `Expected isSameDomain result to be "${expectedResult.toString()}", got "${result.toString()}"` 46 | ); 47 | } 48 | }); 49 | 50 | it("should give a negative result for isSameDomain with a different host", () => { 51 | const win = getSameDomainWindow({ 52 | location: { 53 | protocol: window.location.protocol, 54 | host: "foobar.com:12345", 55 | }, 56 | }); 57 | 58 | // $FlowFixMe 59 | Object.defineProperty(win.location, "href", { 60 | get(): string { 61 | return `${win.location.protocol}//${win.location.host}`; 62 | }, 63 | }); 64 | 65 | const result = isSameDomain(win); 66 | const expectedResult = false; 67 | 68 | if (result !== expectedResult) { 69 | throw new Error( 70 | `Expected isSameDomain result to be "${expectedResult.toString()}", got "${result.toString()}"` 71 | ); 72 | } 73 | }); 74 | 75 | it("should give a negative result for isSameDomain with a different protocol and host", () => { 76 | const win = getSameDomainWindow({ 77 | location: { 78 | protocol: "https:", 79 | host: "foobar.com:12345", 80 | }, 81 | }); 82 | 83 | // $FlowFixMe 84 | Object.defineProperty(win.location, "href", { 85 | get(): string { 86 | return `${win.location.protocol}//${win.location.host}`; 87 | }, 88 | }); 89 | 90 | const result = isSameDomain(win); 91 | const expectedResult = false; 92 | 93 | if (result !== expectedResult) { 94 | throw new Error( 95 | `Expected isSameDomain result to be "${expectedResult.toString()}", got "${result.toString()}"` 96 | ); 97 | } 98 | }); 99 | 100 | it("should give a negative result for isSameDomain when an error is thrown on protocol", () => { 101 | const win = getSameDomainWindow({ 102 | location: { 103 | get protocol() { 104 | throw new Error("error"); 105 | }, 106 | host: window.location.host, 107 | }, 108 | }); 109 | 110 | // $FlowFixMe 111 | Object.defineProperty(win.location, "href", { 112 | get(): string { 113 | return `${win.location.protocol}//${win.location.host}`; 114 | }, 115 | }); 116 | 117 | const result = isSameDomain(win); 118 | const expectedResult = false; 119 | 120 | if (result !== expectedResult) { 121 | throw new Error( 122 | `Expected isSameDomain result to be "${expectedResult.toString()}", got "${result.toString()}"` 123 | ); 124 | } 125 | }); 126 | 127 | it("should give a negative result for isSameDomain when an error is thrown on host", () => { 128 | const win = getSameDomainWindow({ 129 | location: { 130 | protocol: window.location.protocol, 131 | get host() { 132 | throw new Error("error"); 133 | }, 134 | }, 135 | }); 136 | 137 | // $FlowFixMe 138 | Object.defineProperty(win.location, "href", { 139 | get(): string { 140 | return `${win.location.protocol}//${win.location.host}`; 141 | }, 142 | }); 143 | 144 | const result = isSameDomain(win); 145 | const expectedResult = false; 146 | 147 | if (result !== expectedResult) { 148 | throw new Error( 149 | `Expected isSameDomain result to be "${expectedResult.toString()}", got "${result.toString()}"` 150 | ); 151 | } 152 | }); 153 | 154 | it("should give a negative result for isSameDomain when location is non-enumerable", () => { 155 | const win = getSameDomainWindow({}); 156 | 157 | Object.defineProperty(win, "location", { 158 | value: { 159 | protocol: window.location.protocol, 160 | host: window.location.host, 161 | }, 162 | enumerable: false, 163 | }); 164 | 165 | // $FlowFixMe 166 | Object.defineProperty(win.location, "href", { 167 | get(): string { 168 | return `${win.location.protocol}//${win.location.host}`; 169 | }, 170 | }); 171 | 172 | const result = isSameDomain(win); 173 | const expectedResult = false; 174 | 175 | if (result !== expectedResult) { 176 | throw new Error( 177 | `Expected isSameDomain result to be "${expectedResult.toString()}", got "${result.toString()}"` 178 | ); 179 | } 180 | }); 181 | 182 | it("should give a positive result for isSameDomain when mockDomain matches", () => { 183 | window.mockDomain = "mock://foobar.com:12345"; 184 | 185 | const win = getSameDomainWindow({ 186 | location: { 187 | protocol: window.location.protocol, 188 | host: window.location.host, 189 | }, 190 | mockDomain: "mock://foobar.com:12345", 191 | }); 192 | 193 | // $FlowFixMe 194 | Object.defineProperty(win.location, "href", { 195 | get(): string { 196 | return `${win.location.protocol}//${win.location.host}`; 197 | }, 198 | }); 199 | 200 | const result = isSameDomain(win); 201 | const expectedResult = true; 202 | 203 | if (result !== expectedResult) { 204 | throw new Error( 205 | `Expected isSameDomain result to be "${expectedResult.toString()}", got "${result.toString()}"` 206 | ); 207 | } 208 | 209 | delete window.mockDomain; 210 | }); 211 | 212 | it("should give a negative result for isSameDomain when mockDomain does not match", () => { 213 | window.mockDomain = "mock://fizzbuzz.com:345"; 214 | 215 | const win = getSameDomainWindow({ 216 | location: { 217 | protocol: window.location.protocol, 218 | host: window.location.host, 219 | }, 220 | mockDomain: "mock://foobar.com:12345", 221 | }); 222 | 223 | // $FlowFixMe 224 | Object.defineProperty(win.location, "href", { 225 | get(): string { 226 | return `${win.location.protocol}//${win.location.host}`; 227 | }, 228 | }); 229 | 230 | const result = isSameDomain(win); 231 | const expectedResult = false; 232 | 233 | if (result !== expectedResult) { 234 | throw new Error( 235 | `Expected isSameDomain result to be "${expectedResult.toString()}", got "${result.toString()}"` 236 | ); 237 | } 238 | 239 | delete window.mockDomain; 240 | }); 241 | }); 242 | -------------------------------------------------------------------------------- /test/tests/matchDomain.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { assert } from "chai"; 4 | 5 | import { matchDomain } from "../../src"; 6 | 7 | describe("match domain", () => { 8 | describe("returns true when", () => { 9 | it("domain is a string and origin is a string", () => { 10 | assert.isTrue(matchDomain(["http://domain"], "http://domain")); 11 | }); 12 | 13 | it("domain is regex and origin is a string", () => { 14 | assert.isTrue(matchDomain(/^http:\/\/domain$/, "http://domain")); 15 | }); 16 | 17 | it("domain is array with a wildcard and origin is a string", () => { 18 | assert.isTrue(matchDomain(["*"], "http://domain")); 19 | }); 20 | 21 | it("domain is array of strings and origin is a string", () => { 22 | assert.isTrue(matchDomain(["http://domain"], "http://domain")); 23 | }); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /test/tests/stringifyDomainPattern.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { expect } from "chai"; 4 | 5 | import { stringifyDomainPattern } from "../../src"; 6 | 7 | describe("stringifyDomainPattern test cases ", () => { 8 | it("should stringify a single regex expression to RegExp", () => { 9 | const pattern = /[a-zA-Z0-9]{1,5}/; 10 | const domainPatternStringified = stringifyDomainPattern(pattern); 11 | 12 | const expected = `RegExp(${pattern.toString()})`; 13 | 14 | if (typeof domainPatternStringified !== "string") { 15 | throw new TypeError( 16 | `Expected domainPattern to be string, instead got ${typeof domainPatternStringified}` 17 | ); 18 | } 19 | 20 | expect(domainPatternStringified).to.equal(expected); 21 | }); 22 | 23 | it("should stringify an array of domain patterns to RegExp", () => { 24 | const p1 = "/[a-zA-Z0-9]{1,5}/"; 25 | const p2 = "/\\.[a-zA-Z]{2,}$/"; 26 | const domainPatternsArray = [p1, p2]; 27 | 28 | const domainPatternsArrayStringified = 29 | stringifyDomainPattern(domainPatternsArray); 30 | 31 | const expected = `(${p1} | ${p2})`; 32 | 33 | expect(domainPatternsArrayStringified).to.equal(expected); 34 | }); 35 | }); 36 | -------------------------------------------------------------------------------- /test/util.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | window.console.karma = (...args) => { 4 | const karma = 5 | window.karma || 6 | (window.top && window.top.karma) || 7 | (window.opener && window.opener.karma); 8 | karma.log("debug", ...args); 9 | // eslint-disable-next-line no-console 10 | console.log.apply(console, args); 11 | }; 12 | -------------------------------------------------------------------------------- /test/win.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import type { CrossDomainWindowType, SameDomainWindowType } from "../src/types"; 4 | 5 | export function getCrossDomainWindow(options: Object): CrossDomainWindowType { 6 | return { 7 | ...options, 8 | }; 9 | } 10 | 11 | export function getSameDomainWindow(options: Object): SameDomainWindowType { 12 | return { 13 | ...options, 14 | }; 15 | } 16 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | /* eslint import/no-nodejs-modules: off */ 3 | 4 | import type { WebpackConfig } from "@krakenjs/webpack-config-grumbler/index.flow"; 5 | import { getWebpackConfig } from "@krakenjs/webpack-config-grumbler"; 6 | 7 | let FILE_NAME = "cross-domain-utils"; 8 | let MODULE_NAME = "crossDomainUtils"; 9 | 10 | export let WEBPACK_CONFIG: WebpackConfig = getWebpackConfig({ 11 | filename: `${FILE_NAME}.js`, 12 | modulename: MODULE_NAME, 13 | minify: false, 14 | vars: { 15 | __MIN__: false, 16 | __TEST__: false, 17 | }, 18 | }); 19 | 20 | export let WEBPACK_CONFIG_MIN: WebpackConfig = getWebpackConfig({ 21 | filename: `${FILE_NAME}.min.js`, 22 | modulename: MODULE_NAME, 23 | minify: true, 24 | vars: { 25 | __MIN__: true, 26 | __TEST__: false, 27 | }, 28 | }); 29 | 30 | export let WEBPACK_CONFIG_TEST: WebpackConfig = getWebpackConfig({ 31 | modulename: MODULE_NAME, 32 | options: { 33 | devtool: "inline-source-map", 34 | }, 35 | vars: { 36 | __TEST__: true, 37 | }, 38 | }); 39 | 40 | export default [WEBPACK_CONFIG, WEBPACK_CONFIG_MIN]; 41 | --------------------------------------------------------------------------------