1
// Copyright 2008, Google Inc.
3
// Redistribution and use in source and binary forms, with or without
4
// modification, are permitted provided that the following conditions are met:
6
// 1. Redistributions of source code must retain the above copyright notice,
7
// this list of conditions and the following disclaimer.
8
// 2. Redistributions in binary form must reproduce the above copyright notice,
9
// this list of conditions and the following disclaimer in the documentation
10
// and/or other materials provided with the distribution.
11
// 3. Neither the name of Google Inc. nor the names of its contributors may be
12
// used to endorse or promote products derived from this software without
13
// specific prior written permission.
15
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
16
// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
17
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
18
// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
19
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
21
// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
22
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
23
// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
24
// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
struct JSContext; // must declare this before including nsIJSContextStack.h
28
#include <gecko_sdk/include/nsCOMPtr.h>
29
#include <gecko_sdk/include/nsIDOMHTMLInputElement.h>
30
#include <gecko_sdk/include/nsIFile.h>
31
#include <gecko_internal/nsIJSContextStack.h>
32
#include <gecko_internal/nsIXPConnect.h>
33
#elif BROWSER_IEMOBILE
34
#include <webvw.h> // For IPIEHTMLInputTextElement
37
#include "gears/base/common/js_dom_element.h"
39
#include "gears/base/common/leak_counter.h"
42
#include "gears/base/firefox/ns_file_utils.h"
46
JsDomElement::JsDomElement()
47
: is_initialized_(false) {
48
LEAK_COUNTER_INCREMENT(JsDomElement);
52
JsDomElement::~JsDomElement() {
53
LEAK_COUNTER_DECREMENT(JsDomElement);
60
// The IIDs for nsIContent in different versions of Firefox/Gecko.
61
// TODO(michaeln): Add to this list as new versions show up.
65
#define NS_ICONTENT_IID_GECKO190 \
66
{ 0x0acd0482, 0x09a2, 0x42fd, \
67
{ 0xb6, 0x1b, 0x95, 0xa2, 0x01, 0x6a, 0x55, 0xd3 } }
70
#define NS_ICONTENT_IID_GECKO180 \
71
{ 0x3fecc374, 0x2839, 0x4db3, \
72
{ 0x8d, 0xe8, 0x6b, 0x76, 0xd1, 0xd8, 0xe6, 0xf6 } }
74
#define NS_ICONTENT_IID_GECKO181 \
75
{ 0x9d059608, 0xddb0, 0x4e6a, \
76
{ 0x99, 0x69, 0xd2, 0xf3, 0x63, 0xa1, 0xb5, 0x57 } }
79
static const nsIID kPossibleNsContentIIDs[] = {
81
NS_ICONTENT_IID_GECKO190,
83
NS_ICONTENT_IID_GECKO180,
84
NS_ICONTENT_IID_GECKO181,
90
static PRBool StringBeginsWith(const nsAString &source,
91
const nsAString &substring) {
92
nsAString::size_type src_len = source.Length();
93
nsAString::size_type sub_len = substring.Length();
94
if (sub_len > src_len)
96
return Substring(source, 0, sub_len).Equals(substring);
101
// This is a security measure to prevent script from spoofing DOM elements.
102
static bool VerifyNsContent(nsISupports *unknown) {
103
if (!unknown) return false;
106
nsCOMPtr<nsIInterfaceInfoManager> iface_info_manager;
107
iface_info_manager = do_GetService(NS_INTERFACEINFOMANAGER_SERVICE_CONTRACTID,
109
if (NS_FAILED(rv) || !iface_info_manager) { return false; }
111
// The nsIContentIID is version dependent, we test for all we know about
112
for (size_t i = 0; i < ARRAYSIZE(kPossibleNsContentIIDs); ++i) {
113
const nsIID *ns_content_iid = &kPossibleNsContentIIDs[i];
115
// Paranoia, ensure that the IID we query for is either unknown to the
116
// interface manager or not scriptable. The XPConnect JSWrapper
117
// QueryInterface implementation will not forward to script for such
118
// interfaces. In Firefox 2.0 and 1.5, nsIContent is not known by
119
// the interface manager.
120
nsCOMPtr<nsIInterfaceInfo> iface_info;
121
rv = iface_info_manager->GetInfoForIID(ns_content_iid,
122
getter_AddRefs(iface_info));
123
if (NS_SUCCEEDED(rv) && iface_info) {
124
PRBool is_scriptable = PR_TRUE;
125
rv = iface_info->IsScriptable(&is_scriptable);
126
if (NS_FAILED(rv) || is_scriptable) {
127
continue; // Don't test for this interface id
131
// Test if our 'unknown' argument implements nsIContent,
132
// a positive test indicates 'unknown' is not script based.
133
nsCOMPtr<nsISupports> nscontent;
134
rv = unknown->QueryInterface(*ns_content_iid,
135
reinterpret_cast<void**>(&nscontent));
136
if (NS_SUCCEEDED(rv) && nscontent) {
145
bool JsDomElement::InitJsDomElement(JsContextPtr context, JsToken token) {
146
assert(!is_initialized_);
147
assert(!dom_html_element_);
149
if (!JSVAL_IS_OBJECT(token)) { return false; }
150
JSObject *obj = JSVAL_TO_OBJECT(token);
153
nsCOMPtr<nsIXPConnect> xpc;
154
xpc = do_GetService("@mozilla.org/js/xpc/XPConnect;1", &nr);
155
if (NS_FAILED(nr)) { return false; }
157
nsCOMPtr<nsIXPConnectWrappedNative> wrapper;
158
nr = xpc->GetWrappedNativeOfJSObject(context, obj, getter_AddRefs(wrapper));
159
if (NS_FAILED(nr)) { return false; }
161
nsCOMPtr<nsISupports> supports;
162
nr = wrapper->GetNative(getter_AddRefs(supports));
163
if (NS_FAILED(nr)) { return false; }
165
// Verify unknown is a DOM HTML element
166
if (!VerifyNsContent(supports)) { return false; }
167
dom_html_element_ = do_QueryInterface(supports);
168
if (dom_html_element_.get() == NULL) { return false; }
170
is_initialized_ = true;
176
// Security rights are gleaned from the JavaScript context.
177
// "UniversalFileRead" is required to get/set the file input value.
178
// Temporarily pushing NULL onto the JavaScript context stack lets
179
// the system know that native code is running and all rights are granted.
180
class ScopedUniversalReadRights {
182
~ScopedUniversalReadRights() {
187
stack_ = do_GetService("@mozilla.org/js/xpc/ContextStack;1");
188
if (!stack_) return false;
192
void ReleaseRights() {
195
stack_->Pop(¬_used);
200
nsCOMPtr<nsIJSContextStack> stack_;
204
bool JsDomElement::GetFileInputElementValue(
205
std::string16 *file_name_out) {
206
assert(is_initialized_);
207
nsCOMPtr<nsIDOMHTMLInputElement> input = do_QueryInterface(dom_html_element_);
214
ScopedUniversalReadRights rights;
215
if (!rights.GetRights() ||
216
input->GetValue(filepath) != NS_OK) {
221
// If its really a file url, handle it differently. Gecko handles file
222
// input elements in this way when submitting forms.
223
if (StringBeginsWith(filepath, NS_LITERAL_STRING("file:"))) {
224
nsCOMPtr<nsIFile> file;
225
// Converts the URL string into the corresponding nsIFile if possible.
226
NSFileUtils::GetFileFromURLSpec(filepath, getter_AddRefs(file));
227
if (!file || NS_FAILED(file->GetPath(filepath))) {
232
*file_name_out = filepath.get();
237
bool JsDomElement::SetFileInputElementValue(std::string16 &file_name) {
238
assert(is_initialized_);
239
nsCOMPtr<nsIDOMHTMLInputElement> input = do_QueryInterface(dom_html_element_);
244
ScopedUniversalReadRights rights;
245
if (!rights.GetRights() ||
246
input->SetValue(nsString(file_name.c_str())) != NS_OK) {
253
#elif BROWSER_IE || BROWSER_IEMOBILE
256
bool JsDomElement::InitJsDomElement(JsContextPtr context, JsToken token) {
257
assert(!is_initialized_);
260
if (token.vt != VT_DISPATCH) { return false; }
261
IDispatch *dispatch = token.pdispVal;
263
#ifdef BROWSER_IEMOBILE
264
CComQIPtr<IPIEHTMLElement> html_element(dispatch);
266
CComQIPtr<IHTMLElement> html_element(dispatch);
268
if (!html_element) { return false; }
270
dispatch_ = dispatch;
271
is_initialized_ = true;
276
bool JsDomElement::GetFileInputElementValue(
277
std::string16 *file_name_out) {
278
assert(is_initialized_);
279
#ifdef BROWSER_IEMOBILE
280
// If it implements the IPIEHTMLInputTextElement interface, and has type
281
// 'file', then accept it.
282
CComQIPtr<IPIEHTMLInputTextElement> input(dispatch_);
284
if (input && (FAILED(input->get_type(&type)) || type != L"file")) {
288
// If it implements the IHTMLInputFileElemement interface, then accept it.
289
CComQIPtr<IHTMLInputFileElement> input(dispatch_);
295
if (FAILED(input->get_value(&filepath))) {
298
if (filepath.m_str) {
299
file_name_out->assign(filepath);
301
file_name_out->clear();
307
bool JsDomElement::SetFileInputElementValue(std::string16 &file_name) {
308
assert(is_initialized_);
309
// On IE, we don't seem to be able to set the value of a file input element,
310
// presumably because it clashes with IE's security model. Consequently,
311
// any Gears code, such as LocalServer's FileSubmitter, that would normally
312
// call this method also needs to implement a work-around on IE.
320
bool JsDomElement::InitJsDomElement(JsContextPtr context, JsToken token) {
321
// A DOM element is at least an NPObject, but an NPObject can also be
322
// an arbitrary javascript object. So this is not a strong test, just
323
// a hint that the token might be a DOM element.
324
assert(!is_initialized_);
325
is_initialized_ = JsTokenToObject_NoCoerce(token, context,
326
as_out_parameter(js_object_));
327
return is_initialized_;
331
bool JsDomElement::GetFileInputElementValue(
332
std::string16 *file_name_out) {
333
// TODO(nigeltao): implement on NPAPI.
334
assert(is_initialized_);
339
bool JsDomElement::SetFileInputElementValue(std::string16 &file_name) {
340
// TODO(nigeltao): implement on NPAPI.
341
assert(is_initialized_);