OLD | NEW |
| (Empty) |
1 // Copyright 2010 The Ginsu Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can | |
3 // be found in the LICENSE file. | |
4 | |
5 #include "c_salt/scripting_bridge.h" | |
6 | |
7 #include <cassert> | |
8 #include <cstring> | |
9 #include <string> | |
10 | |
11 #include "boost/scoped_ptr.hpp" | |
12 #include "c_salt/callback.h" | |
13 #include "c_salt/instance.h" | |
14 #include "c_salt/npapi/browser_binding.h" | |
15 #include "c_salt/variant.h" | |
16 | |
17 using c_salt::npapi::BrowserBinding; | |
18 | |
19 namespace c_salt { | |
20 | |
21 ScriptingBridge::ScriptingBridge(BrowserBinding* browser_binding) | |
22 : browser_binding_(browser_binding), | |
23 window_object_(NULL) { | |
24 } | |
25 | |
26 ScriptingBridge::~ScriptingBridge() { | |
27 ReleaseBrowserBinding(); | |
28 if (window_object_) { | |
29 NPN_ReleaseObject(window_object_); | |
30 window_object_ = NULL; | |
31 } | |
32 } | |
33 | |
34 NPObject* ScriptingBridge::CopyBrowserBinding() { | |
35 if (browser_binding_) browser_binding_->Retain(); | |
36 return browser_binding_; | |
37 } | |
38 | |
39 void ScriptingBridge::ReleaseBrowserBinding() { | |
40 if (browser_binding_) { | |
41 bool will_dealloc = browser_binding_->referenceCount == 1; | |
42 BrowserBinding* tmp_binding = browser_binding_; | |
43 browser_binding_ = NULL; | |
44 NPN_ReleaseObject(tmp_binding); | |
45 // |this| might be deallocated at this point. Further calls might have | |
46 // unpredictable results. | |
47 if (!will_dealloc) { | |
48 browser_binding_ = tmp_binding; | |
49 } | |
50 } | |
51 // |this| might be deallocated at this point. Further calls might have | |
52 // unpredictable results. | |
53 } | |
54 | |
55 bool ScriptingBridge::LogToConsole(const std::string& msg) const { | |
56 bool success = false; | |
57 NPObject* window = window_object(); | |
58 if (window) { | |
59 static const char* kConsoleAccessor = "top.console"; | |
60 NPString console_script = { 0 }; | |
61 console_script.UTF8Length = strlen(kConsoleAccessor); | |
62 console_script.UTF8Characters = kConsoleAccessor; | |
63 NPVariant console; | |
64 if (NPN_Evaluate(GetBrowserInstance(), | |
65 window, | |
66 &console_script, | |
67 &console)) { | |
68 if (NPVARIANT_IS_OBJECT(console)) { | |
69 // Convert the message to NPString; | |
70 NPVariant text; | |
71 STRINGN_TO_NPVARIANT(msg.c_str(), msg.size(), text); | |
72 NPVariant result; | |
73 if (NPN_Invoke(GetBrowserInstance(), | |
74 NPVARIANT_TO_OBJECT(console), | |
75 NPN_GetStringIdentifier("log"), | |
76 &text, | |
77 1, | |
78 &result)) { | |
79 NPN_ReleaseVariantValue(&result); | |
80 success = true; | |
81 } | |
82 } | |
83 NPN_ReleaseVariantValue(&console); | |
84 } | |
85 } | |
86 return success; | |
87 } | |
88 | |
89 const NPP& ScriptingBridge::GetBrowserInstance() const { | |
90 return static_cast<const BrowserBinding*>(browser_binding_)->npp(); | |
91 } | |
92 | |
93 NPObject* ScriptingBridge::window_object() const { | |
94 if (!window_object_) { | |
95 NPN_GetValue(GetBrowserInstance(), | |
96 NPNVWindowNPObject, | |
97 &window_object_); | |
98 } | |
99 return window_object_; | |
100 } | |
101 | |
102 void ScriptingBridge::Invalidate() { | |
103 // This is called from the browser after NPP_Delete, on all objects with | |
104 // dangling references. | |
105 method_dictionary_.clear(); | |
106 property_dictionary_.clear(); | |
107 // Unhook the weak reference. | |
108 browser_binding_ = NULL; | |
109 } | |
110 | |
111 bool ScriptingBridge::HasScriptMethod(const std::string& name) { | |
112 MethodDictionary::const_iterator i; | |
113 i = method_dictionary_.find(name); | |
114 return i != method_dictionary_.end(); | |
115 } | |
116 | |
117 bool | |
118 ScriptingBridge::InvokeScriptMethod(const std::string& method_name, | |
119 const SharedVariant* params_begin, | |
120 const SharedVariant* params_end, | |
121 ::c_salt::SharedVariant* return_value) { | |
122 MethodDictionary::iterator i; | |
123 i = method_dictionary_.find(method_name); | |
124 if (i != method_dictionary_.end()) { | |
125 return (*i->second).Execute(params_begin, | |
126 params_end, | |
127 return_value); | |
128 } | |
129 return false; | |
130 } | |
131 | |
132 void ScriptingBridge::GetAllPropertyNames(std::vector<std::string>* prop_names) | |
133 const { | |
134 if (prop_names) { | |
135 // Use the copy-and-swap idiom to ensure prop_names is only modified if | |
136 // everything succeeds. | |
137 std::vector<std::string> keys; | |
138 keys.reserve(method_dictionary_.size()); | |
139 MethodDictionary::const_iterator iter(method_dictionary_.begin()), | |
140 the_end(method_dictionary_.end()); | |
141 for (; iter != the_end; ++iter) { | |
142 keys.push_back(iter->first); | |
143 } | |
144 prop_names->swap(keys); | |
145 } | |
146 } | |
147 | |
148 bool ScriptingBridge::AddProperty(const Property& property) { | |
149 if (property.name().empty()) | |
150 return false; | |
151 property_dictionary_.insert(PropertyDictionary::value_type(property.name(), | |
152 property)); | |
153 return true; | |
154 } | |
155 | |
156 bool ScriptingBridge::GetValueForPropertyNamed(const std::string& name, | |
157 SharedVariant* value) const { | |
158 assert(value); | |
159 if (NULL == value) { | |
160 return false; | |
161 } | |
162 return GetScriptProperty(name, value); | |
163 } | |
164 | |
165 bool ScriptingBridge::SetValueForPropertyNamed(const std::string& name, | |
166 const Variant& value) { | |
167 SharedVariant shared_value(new Variant(value)); | |
168 return SetScriptProperty(name, shared_value); | |
169 } | |
170 | |
171 bool ScriptingBridge::HasScriptProperty(const std::string& name) { | |
172 // If |name| is not a property, then it *could* be a method. Consider this | |
173 // JavaScript: | |
174 // module.method(); | |
175 // In NPAPI, the browser first calls HasProperty("method"), and if it gets a | |
176 // |false| return it then calls HasMethod("method"). That means this | |
177 // function should return |false| _only_ if |name| is a valid method name. | |
178 // In all other cases, it returns |true|. The |true| return will | |
179 // cause the browser to attempt either a GetProperty() or a SetProperty(). | |
180 // If a Get is attempted on a non-existent property, then GetProperty() | |
181 // method will return |false| and the JavaScript will (correctly) get an | |
182 // undefined object. Alternatively, if the browser attempts a Set then | |
183 // SetProperty will update an existing property or insert it as a new dynamic | |
184 // if it doesn't exist. | |
185 bool has_method = HasScriptMethod(name); | |
186 // Writing this out long-hand so hopefully it's more understandable. | |
187 return has_method ? false : true; | |
188 } | |
189 | |
190 bool ScriptingBridge::GetScriptProperty(const std::string& name, | |
191 SharedVariant* return_value) const { | |
192 assert(return_value); | |
193 if (NULL == return_value) { | |
194 return false; | |
195 } | |
196 if (!name.empty()) { | |
197 PropertyDictionary::const_iterator iter = property_dictionary_.find(name); | |
198 if (iter != property_dictionary_.end()) { | |
199 *return_value = (iter->second.GetValue()); | |
200 return true; | |
201 } | |
202 } | |
203 return_value->reset(new Variant()); | |
204 return false; | |
205 } | |
206 | |
207 bool ScriptingBridge::SetScriptProperty(const std::string& name, | |
208 const SharedVariant& value) { | |
209 if (name.empty()) | |
210 return false; | |
211 PropertyDictionary::iterator iter = property_dictionary_.find(name); | |
212 if (iter != property_dictionary_.end()) { | |
213 if (iter->second.is_mutable()) { | |
214 iter->second.SetValue(value); | |
215 return true; | |
216 } | |
217 // It was not mutable, so disallow assignment. | |
218 return false; | |
219 } | |
220 // No property was found. Create one and add it. Note that properties | |
221 // created by the browser are always dynamic and mutable. | |
222 PropertyAttributes prop_attrs(name, value); | |
223 prop_attrs.set_dynamic().set_mutable(); | |
224 property_dictionary_.insert( | |
225 PropertyDictionary::value_type(name, Property(prop_attrs))); | |
226 return true; | |
227 } | |
228 | |
229 bool ScriptingBridge::RemoveScriptProperty(const std::string& name) { | |
230 // If the property exists and is dynamic, delete it. | |
231 PropertyDictionary::iterator iter = property_dictionary_.find(name); | |
232 if (iter != property_dictionary_.end()) { | |
233 if (!iter->second.is_static()) { | |
234 property_dictionary_.erase(iter); | |
235 return true; | |
236 } | |
237 } | |
238 return false; | |
239 } | |
240 | |
241 } // namespace c_salt | |
OLD | NEW |