/** * @license * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ import { DOCUMENT } from '@angular/common'; import { Inject, Injectable } from '@angular/core'; import { Observable } from 'rxjs'; import { HttpErrorResponse, HttpEventType, HttpResponse } from './response'; // Every request made through JSONP needs a callback name that's unique across the // whole page. Each request is assigned an id and the callback name is constructed // from that. The next id to be assigned is tracked in a global variable here that // is shared among all applications on the page. let nextRequestId = 0; // Error text given when a JSONP script is injected, but doesn't invoke the callback // passed in its URL. export const JSONP_ERR_NO_CALLBACK = 'JSONP injected script did not invoke callback.'; // Error text given when a request is passed to the JsonpClientBackend that doesn't // have a request method JSONP. export const JSONP_ERR_WRONG_METHOD = 'JSONP requests must use JSONP request method.'; export const JSONP_ERR_WRONG_RESPONSE_TYPE = 'JSONP requests must use Json response type.'; /** * DI token/abstract type representing a map of JSONP callbacks. * * In the browser, this should always be the `window` object. * * */ export class JsonpCallbackContext { } /** * Processes an `HttpRequest` with the JSONP method, * by performing JSONP style requests. * @see `HttpHandler` * @see `HttpXhrBackend` * * @publicApi */ export class JsonpClientBackend { constructor(callbackMap, document) { this.callbackMap = callbackMap; this.document = document; /** * A resolved promise that can be used to schedule microtasks in the event handlers. */ this.resolvedPromise = Promise.resolve(); } /** * Get the name of the next callback method, by incrementing the global `nextRequestId`. */ nextCallback() { return `ng_jsonp_callback_${nextRequestId++}`; } /** * Processes a JSONP request and returns an event stream of the results. * @param req The request object. * @returns An observable of the response events. * */ handle(req) { // Firstly, check both the method and response type. If either doesn't match // then the request was improperly routed here and cannot be handled. if (req.method !== 'JSONP') { throw new Error(JSONP_ERR_WRONG_METHOD); } else if (req.responseType !== 'json') { throw new Error(JSONP_ERR_WRONG_RESPONSE_TYPE); } // Everything else happens inside the Observable boundary. return new Observable((observer) => { // The first step to make a request is to generate the callback name, and replace the // callback placeholder in the URL with the name. Care has to be taken here to ensure // a trailing &, if matched, gets inserted back into the URL in the correct place. const callback = this.nextCallback(); const url = req.urlWithParams.replace(/=JSONP_CALLBACK(&|$)/, `=${callback}$1`); // Construct the