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