// 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 WebKit::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(WebKit::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; }