普通文本  |  194行  |  6.57 KB

// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/renderer/external_host_bindings.h"

#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/values.h"
#include "chrome/common/render_messages.h"
#include "third_party/WebKit/public/web/WebBindings.h"
#include "third_party/WebKit/public/web/WebDocument.h"
#include "third_party/WebKit/public/web/WebFrame.h"

using blink::WebBindings;
using webkit_glue::CppArgumentList;
using webkit_glue::CppVariant;

ExternalHostBindings::ExternalHostBindings(IPC::Sender* sender, int routing_id)
    : frame_(NULL), sender_(sender), routing_id_(routing_id) {
  BindCallback("postMessage", base::Bind(&ExternalHostBindings::PostMessage,
                                         base::Unretained(this)));
  BindProperty("onmessage", &on_message_handler_);
}

ExternalHostBindings::~ExternalHostBindings() {
}

void ExternalHostBindings::PostMessage(
    const CppArgumentList& args, CppVariant* result) {
  DCHECK(result);

  // We need at least one argument (message) and at most 2 arguments.
  // Also, the first argument must be a string
  if (args.size() < 1 || args.size() > 2 || !args[0].isString()) {
    result->Set(false);
    return;
  }

  const std::string& message = args[0].ToString();
  std::string target;
  if (args.size() >= 2 && args[1].isString()) {
    target = args[1].ToString();
    if (target.compare("*") != 0) {
      GURL resolved(target);
      if (!resolved.is_valid()) {
        DLOG(WARNING) << "Unable to parse the specified target URL. " << target;
        result->Set(false);
        return;
      }
      target = resolved.spec();
    }
  } else {
    target = "*";
  }

  std::string origin = frame_->document().securityOrigin().toString().utf8();

  result->Set(sender_->Send(
      new ChromeViewHostMsg_ForwardMessageToExternalHost(
          routing_id_, message, origin, target)));
}

bool ExternalHostBindings::ForwardMessageFromExternalHost(
    const std::string& message, const std::string& origin,
    const std::string& target) {
  if (!on_message_handler_.isObject())
    return false;

  bool status = false;

  if (target.compare("*") != 0) {
    // TODO(abarth): This code should use WebSecurityOrigin::toString to
    // make origin strings. GURL::GetOrigin() doesn't understand all the
    // cases that WebSecurityOrigin::toString understands.
    GURL document_url(frame_->document().url());
    GURL document_origin(document_url.GetOrigin());
    GURL target_origin(GURL(target).GetOrigin());

    // We want to compare the origins of the two URLs but first
    // we need to make sure that we don't compare an invalid one
    // to a valid one.
    bool drop = (document_origin.is_valid() != target_origin.is_valid());

    if (!drop) {
      if (!document_origin.is_valid()) {
        // Both origins are invalid, so compare the URLs as opaque strings.
        drop = (document_url.spec().compare(target) != 0);
      } else {
        drop = (document_origin != target_origin);
      }
    }

    if (drop) {
      DLOG(WARNING) << "Dropping posted message.  Origins don't match";
      return false;
    }
  }

  // Construct an event object, assign the origin to the origin member and
  // assign message parameter to the 'data' member of the event.
  NPObject* event_obj = NULL;
  CreateMessageEvent(&event_obj);
  if (!event_obj) {
    NOTREACHED() << "CreateMessageEvent failed";
  } else {
    NPIdentifier init_message_event =
        WebBindings::getStringIdentifier("initMessageEvent");
    NPVariant init_args[8];
    STRINGN_TO_NPVARIANT("message", sizeof("message") - 1,
                         init_args[0]);  // type
    BOOLEAN_TO_NPVARIANT(false, init_args[1]);  // canBubble
    BOOLEAN_TO_NPVARIANT(true, init_args[2]);  // cancelable
    STRINGN_TO_NPVARIANT(message.c_str(), message.length(), \
                         init_args[3]);  // data
    STRINGN_TO_NPVARIANT(origin.c_str(), origin.length(), \
                         init_args[4]);  // origin
    STRINGN_TO_NPVARIANT("", 0, init_args[5]);  // lastEventId
    NULL_TO_NPVARIANT(init_args[6]);  // source
    NULL_TO_NPVARIANT(init_args[7]);  // messagePort

    NPVariant result;
    NULL_TO_NPVARIANT(result);
    status = WebBindings::invoke(NULL, event_obj, init_message_event, init_args,
                                 arraysize(init_args), &result);
    DCHECK(status) << "Failed to initialize MessageEvent";
    WebBindings::releaseVariantValue(&result);

    if (status) {
      NPVariant event_arg;
      OBJECT_TO_NPVARIANT(event_obj, event_arg);
      status = WebBindings::invokeDefault(NULL,
                                          on_message_handler_.value.objectValue,
                                          &event_arg, 1, &result);
      // Don't DCHECK here in case the reason for the failure is a script error.
      DLOG_IF(ERROR, !status) << "NPN_InvokeDefault failed";
      WebBindings::releaseVariantValue(&result);
    }

    WebBindings::releaseObject(event_obj);
  }

  return status;
}

void ExternalHostBindings::BindToJavascript(blink::WebFrame* frame,
                                            const std::string& classname) {
  frame_ = frame;
  CppBoundClass::BindToJavascript(frame, classname);
}

bool ExternalHostBindings::CreateMessageEvent(NPObject** message_event) {
  DCHECK(message_event != NULL);
  DCHECK(frame_ != NULL);

  NPObject* window = frame_->windowObject();
  if (!window) {
    NOTREACHED() << "frame_->windowObject";
    return false;
  }

  const char* identifier_names[] = {
    "document",
    "createEvent",
  };

  NPIdentifier identifiers[arraysize(identifier_names)] = {0};
  WebBindings::getStringIdentifiers(identifier_names,
                                    arraysize(identifier_names), identifiers);

  CppVariant document;
  bool ok = WebBindings::getProperty(NULL, window, identifiers[0], &document);
  DCHECK(document.isObject());

  bool success = false;
  if (ok && document.isObject()) {
    NPVariant result, event_type;
    STRINGN_TO_NPVARIANT("MessageEvent", sizeof("MessageEvent") - 1, \
                         event_type);
    success = WebBindings::invoke(NULL, document.value.objectValue,
                                  identifiers[1], &event_type, 1, &result);
    DCHECK(!success || result.type == NPVariantType_Object);
    if (result.type != NPVariantType_Object) {
      DCHECK(success == false);
    } else {
      DCHECK(success != false);
      // Pass the ownership to the caller (don't call ReleaseVariantValue).
      *message_event = result.value.objectValue;
    }
  }

  return success;
}