// Copyright (c) 2006-2008 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/values.h" #include "chrome/common/render_messages.h" #include "third_party/WebKit/WebKit/chromium/public/WebBindings.h" #include "third_party/WebKit/WebKit/chromium/public/WebFrame.h" using WebKit::WebBindings; ExternalHostBindings::ExternalHostBindings() : frame_(NULL) { BindMethod("postMessage", &ExternalHostBindings::postMessage); BindProperty("onmessage", &on_message_handler_); } 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; GURL origin_url(GURL(frame_->url()).GetOrigin()); if (origin_url.is_empty()) { // If the origin is not a scheme/host/port tuple, then return the literal // string "null". origin = "null"; } else { origin = origin_url.spec(); } result->Set(sender()->Send(new ViewHostMsg_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) { GURL frame_url(frame_->url()); GURL frame_origin(frame_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 = (frame_origin.is_valid() != target_origin.is_valid()); if (!drop) { if (!frame_origin.is_valid()) { // Both origins are invalid, so compare the URLs as opaque strings. drop = (frame_url.spec().compare(target) != 0); } else { drop = (frame_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; } 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; }