chromium/extensions/renderer/resources/mime_handler_private_custom_bindings.js

// Copyright 2015 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

/**
 * Custom bindings for the mime handler API.
 */

var exceptionHandler = require('uncaught_exception_handler');
var logActivity = requireNative('activityLogger');

var NO_STREAM_ERROR =
    'Streams are only available from a mime handler view guest.';
var STREAM_ABORTED_ERROR = 'Stream has been aborted.';

if ((typeof mojo === 'undefined') || !mojo.bindingsLibraryInitialized) {
  loadScript('mojo_bindings');
}
loadScript('extensions/common/api/mime_handler.mojom');

// DO NOT USE. This causes problems with safe builtins, and makes migration to
// native bindings more difficult.
function handleRequestWithPromiseDoNotUse(
    binding, apiName, methodName, customizedFunction) {
  var fullName = apiName + '.' + methodName;
  var extensionId = requireNative('process').GetExtensionId();
  binding.setHandleRequest(methodName, function() {
    logActivity.LogAPICall(extensionId, fullName, $Array.slice(arguments));
    var stack = exceptionHandler.getExtensionStackTrace();
    var callback = arguments[arguments.length - 2];
    var args = $Array.slice(arguments, 0, arguments.length - 2);
    var keepAlive = require('keep_alive').createKeepAlive();
    $Function.apply(customizedFunction, this, args).then(function(result) {
      if (callback) {
        exceptionHandler.safeCallbackApply(
            fullName, callback, [result], stack);
      }
    }).catch(function(error) {
      if (callback) {
        var message = exceptionHandler.safeErrorToString(error, true);
        bindingUtil.runCallbackWithLastError(message, callback);
      }
    }).then(function() {
      keepAlive.close();
    });
  });
};

var servicePtr = new extensions.mimeHandler.MimeHandlerServicePtr;
Mojo.bindInterface(
    extensions.mimeHandler.MimeHandlerService.name,
    mojo.makeRequest(servicePtr).handle);
var beforeUnloadControlPtr =
    new extensions.mimeHandler.BeforeUnloadControlPtr;
Mojo.bindInterface(
    extensions.mimeHandler.BeforeUnloadControl.name,
    mojo.makeRequest(beforeUnloadControlPtr).handle);

// Stores a promise to the GetStreamInfo() result to avoid making additional
// calls in response to getStreamInfo() calls.
var streamInfoPromise;

function throwNoStreamError() {
  throw new Error(NO_STREAM_ERROR);
}

function createStreamInfoPromise() {
  return servicePtr.getStreamInfo().then(function(result) {
    if (!result.streamInfo)
      throw new Error(STREAM_ABORTED_ERROR);
    return result.streamInfo;
  }, throwNoStreamError);
}

function constructStreamInfoDict(streamInfo) {
  var headers = {};
  for (var header of streamInfo.responseHeaders) {
    headers[header[0]] = header[1];
  }
  return {
    mimeType: streamInfo.mimeType,
    originalUrl: streamInfo.originalUrl,
    streamUrl: streamInfo.streamUrl,
    tabId: streamInfo.tabId,
    embedded: !!streamInfo.embedded,
    responseHeaders: headers,
  };
}

apiBridge.registerCustomHook(function(bindingsAPI) {
  var apiFunctions = bindingsAPI.apiFunctions;
  handleRequestWithPromiseDoNotUse(
      apiFunctions, 'mimeHandlerPrivate', 'getStreamInfo',
      function() {
    if (!streamInfoPromise)
      streamInfoPromise = createStreamInfoPromise();
    return streamInfoPromise.then(constructStreamInfoDict);
  });

  apiFunctions.setHandleRequest(
      'setPdfPluginAttributes', function(pdfPluginAttributes) {
        servicePtr.setPdfPluginAttributes(pdfPluginAttributes);
      });

  handleRequestWithPromiseDoNotUse(
      apiFunctions, 'mimeHandlerPrivate', 'setShowBeforeUnloadDialog',
      function(showDialog) {
    return beforeUnloadControlPtr.setShowBeforeUnloadDialog(showDialog);
  });
});