Home | History | Annotate | Download | only in renderer
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "chrome/renderer/external_host_bindings.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/bind_helpers.h"
      9 #include "base/values.h"
     10 #include "chrome/common/render_messages.h"
     11 #include "third_party/WebKit/public/web/WebBindings.h"
     12 #include "third_party/WebKit/public/web/WebDocument.h"
     13 #include "third_party/WebKit/public/web/WebFrame.h"
     14 
     15 using WebKit::WebBindings;
     16 using webkit_glue::CppArgumentList;
     17 using webkit_glue::CppVariant;
     18 
     19 ExternalHostBindings::ExternalHostBindings(IPC::Sender* sender, int routing_id)
     20     : frame_(NULL), sender_(sender), routing_id_(routing_id) {
     21   BindCallback("postMessage", base::Bind(&ExternalHostBindings::PostMessage,
     22                                          base::Unretained(this)));
     23   BindProperty("onmessage", &on_message_handler_);
     24 }
     25 
     26 ExternalHostBindings::~ExternalHostBindings() {
     27 }
     28 
     29 void ExternalHostBindings::PostMessage(
     30     const CppArgumentList& args, CppVariant* result) {
     31   DCHECK(result);
     32 
     33   // We need at least one argument (message) and at most 2 arguments.
     34   // Also, the first argument must be a string
     35   if (args.size() < 1 || args.size() > 2 || !args[0].isString()) {
     36     result->Set(false);
     37     return;
     38   }
     39 
     40   const std::string& message = args[0].ToString();
     41   std::string target;
     42   if (args.size() >= 2 && args[1].isString()) {
     43     target = args[1].ToString();
     44     if (target.compare("*") != 0) {
     45       GURL resolved(target);
     46       if (!resolved.is_valid()) {
     47         DLOG(WARNING) << "Unable to parse the specified target URL. " << target;
     48         result->Set(false);
     49         return;
     50       }
     51       target = resolved.spec();
     52     }
     53   } else {
     54     target = "*";
     55   }
     56 
     57   std::string origin = frame_->document().securityOrigin().toString().utf8();
     58 
     59   result->Set(sender_->Send(
     60       new ChromeViewHostMsg_ForwardMessageToExternalHost(
     61           routing_id_, message, origin, target)));
     62 }
     63 
     64 bool ExternalHostBindings::ForwardMessageFromExternalHost(
     65     const std::string& message, const std::string& origin,
     66     const std::string& target) {
     67   if (!on_message_handler_.isObject())
     68     return false;
     69 
     70   bool status = false;
     71 
     72   if (target.compare("*") != 0) {
     73     // TODO(abarth): This code should use WebSecurityOrigin::toString to
     74     // make origin strings. GURL::GetOrigin() doesn't understand all the
     75     // cases that WebSecurityOrigin::toString understands.
     76     GURL document_url(frame_->document().url());
     77     GURL document_origin(document_url.GetOrigin());
     78     GURL target_origin(GURL(target).GetOrigin());
     79 
     80     // We want to compare the origins of the two URLs but first
     81     // we need to make sure that we don't compare an invalid one
     82     // to a valid one.
     83     bool drop = (document_origin.is_valid() != target_origin.is_valid());
     84 
     85     if (!drop) {
     86       if (!document_origin.is_valid()) {
     87         // Both origins are invalid, so compare the URLs as opaque strings.
     88         drop = (document_url.spec().compare(target) != 0);
     89       } else {
     90         drop = (document_origin != target_origin);
     91       }
     92     }
     93 
     94     if (drop) {
     95       DLOG(WARNING) << "Dropping posted message.  Origins don't match";
     96       return false;
     97     }
     98   }
     99 
    100   // Construct an event object, assign the origin to the origin member and
    101   // assign message parameter to the 'data' member of the event.
    102   NPObject* event_obj = NULL;
    103   CreateMessageEvent(&event_obj);
    104   if (!event_obj) {
    105     NOTREACHED() << "CreateMessageEvent failed";
    106   } else {
    107     NPIdentifier init_message_event =
    108         WebBindings::getStringIdentifier("initMessageEvent");
    109     NPVariant init_args[8];
    110     STRINGN_TO_NPVARIANT("message", sizeof("message") - 1,
    111                          init_args[0]);  // type
    112     BOOLEAN_TO_NPVARIANT(false, init_args[1]);  // canBubble
    113     BOOLEAN_TO_NPVARIANT(true, init_args[2]);  // cancelable
    114     STRINGN_TO_NPVARIANT(message.c_str(), message.length(), \
    115                          init_args[3]);  // data
    116     STRINGN_TO_NPVARIANT(origin.c_str(), origin.length(), \
    117                          init_args[4]);  // origin
    118     STRINGN_TO_NPVARIANT("", 0, init_args[5]);  // lastEventId
    119     NULL_TO_NPVARIANT(init_args[6]);  // source
    120     NULL_TO_NPVARIANT(init_args[7]);  // messagePort
    121 
    122     NPVariant result;
    123     NULL_TO_NPVARIANT(result);
    124     status = WebBindings::invoke(NULL, event_obj, init_message_event, init_args,
    125                                  arraysize(init_args), &result);
    126     DCHECK(status) << "Failed to initialize MessageEvent";
    127     WebBindings::releaseVariantValue(&result);
    128 
    129     if (status) {
    130       NPVariant event_arg;
    131       OBJECT_TO_NPVARIANT(event_obj, event_arg);
    132       status = WebBindings::invokeDefault(NULL,
    133                                           on_message_handler_.value.objectValue,
    134                                           &event_arg, 1, &result);
    135       // Don't DCHECK here in case the reason for the failure is a script error.
    136       DLOG_IF(ERROR, !status) << "NPN_InvokeDefault failed";
    137       WebBindings::releaseVariantValue(&result);
    138     }
    139 
    140     WebBindings::releaseObject(event_obj);
    141   }
    142 
    143   return status;
    144 }
    145 
    146 void ExternalHostBindings::BindToJavascript(WebKit::WebFrame* frame,
    147                                             const std::string& classname) {
    148   frame_ = frame;
    149   CppBoundClass::BindToJavascript(frame, classname);
    150 }
    151 
    152 bool ExternalHostBindings::CreateMessageEvent(NPObject** message_event) {
    153   DCHECK(message_event != NULL);
    154   DCHECK(frame_ != NULL);
    155 
    156   NPObject* window = frame_->windowObject();
    157   if (!window) {
    158     NOTREACHED() << "frame_->windowObject";
    159     return false;
    160   }
    161 
    162   const char* identifier_names[] = {
    163     "document",
    164     "createEvent",
    165   };
    166 
    167   NPIdentifier identifiers[arraysize(identifier_names)] = {0};
    168   WebBindings::getStringIdentifiers(identifier_names,
    169                                     arraysize(identifier_names), identifiers);
    170 
    171   CppVariant document;
    172   bool ok = WebBindings::getProperty(NULL, window, identifiers[0], &document);
    173   DCHECK(document.isObject());
    174 
    175   bool success = false;
    176   if (ok && document.isObject()) {
    177     NPVariant result, event_type;
    178     STRINGN_TO_NPVARIANT("MessageEvent", sizeof("MessageEvent") - 1, \
    179                          event_type);
    180     success = WebBindings::invoke(NULL, document.value.objectValue,
    181                                   identifiers[1], &event_type, 1, &result);
    182     DCHECK(!success || result.type == NPVariantType_Object);
    183     if (result.type != NPVariantType_Object) {
    184       DCHECK(success == false);
    185     } else {
    186       DCHECK(success != false);
    187       // Pass the ownership to the caller (don't call ReleaseVariantValue).
    188       *message_event = result.value.objectValue;
    189     }
    190   }
    191 
    192   return success;
    193 }
    194