• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 blink::WebBindings;
16 using webkit_glue::CppArgumentList;
17 using webkit_glue::CppVariant;
18 
ExternalHostBindings(IPC::Sender * sender,int routing_id)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 
~ExternalHostBindings()26 ExternalHostBindings::~ExternalHostBindings() {
27 }
28 
PostMessage(const CppArgumentList & args,CppVariant * result)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 
ForwardMessageFromExternalHost(const std::string & message,const std::string & origin,const std::string & target)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 
BindToJavascript(blink::WebFrame * frame,const std::string & classname)146 void ExternalHostBindings::BindToJavascript(blink::WebFrame* frame,
147                                             const std::string& classname) {
148   frame_ = frame;
149   CppBoundClass::BindToJavascript(frame, classname);
150 }
151 
CreateMessageEvent(NPObject ** message_event)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