"Fossies" - the Fresh Open Source Software Archive

Member "google-gadgets-for-linux-0.11.2/extensions/webkit_script_runtime/converter.cc" (11 May 2009, 28622 Bytes) of package /linux/misc/old/google-gadgets-for-linux-0.11.2.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file.

    1 /*
    2   Copyright 2008 Google Inc.
    3 
    4   Licensed under the Apache License, Version 2.0 (the "License");
    5   you may not use this file except in compliance with the License.
    6   You may obtain a copy of the License at
    7 
    8        http://www.apache.org/licenses/LICENSE-2.0
    9 
   10   Unless required by applicable law or agreed to in writing, software
   11   distributed under the License is distributed on an "AS IS" BASIS,
   12   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   13   See the License for the specific language governing permissions and
   14   limitations under the License.
   15 */
   16 
   17 #include "converter.h"
   18 #include <cmath>
   19 #include <cstdlib>
   20 #include <ggadget/scriptable_array.h>
   21 #include <ggadget/scriptable_binary_data.h>
   22 #include <ggadget/scriptable_holder.h>
   23 #include <ggadget/scriptable_interface.h>
   24 #include <ggadget/string_utils.h>
   25 #include <ggadget/unicode_utils.h>
   26 #include <ggadget/variant.h>
   27 #include <ggadget/js/jscript_massager.h>
   28 #include "js_script_context.h"
   29 #include "json.h"
   30 
   31 using namespace ggadget::js;
   32 
   33 namespace ggadget {
   34 namespace webkit {
   35 
   36 static bool ConvertJSToNativeVoid(JSScriptContext *ctx, JSValueRef js_val,
   37                                   Variant *native_val) {
   38   *native_val = Variant();
   39   return true;
   40 }
   41 
   42 static bool ConvertJSToNativeBool(JSScriptContext *ctx, JSValueRef js_val,
   43                                   Variant *native_val) {
   44   JSContextRef js_ctx = ctx->GetContext();
   45   if (JSValueIsString(js_ctx, js_val)) {
   46     JSValueRef exception = NULL;
   47     JSStringRef str = JSValueToStringCopy(js_ctx, js_val, &exception);
   48     if (!str) {
   49       ctx->CheckJSException(exception);
   50       return false;
   51     }
   52     *native_val = Variant(
   53         JSStringGetLength(str) && !JSStringIsEqualToUTF8CString(str, "false"));
   54     JSStringRelease(str);
   55     return true;
   56   }
   57 
   58   *native_val = Variant(JSValueToBoolean(js_ctx, js_val));
   59   return true;
   60 }
   61 
   62 static bool ConvertJSToNativeInt(JSScriptContext *ctx, JSValueRef js_val,
   63                                  Variant *native_val) {
   64   JSContextRef js_ctx = ctx->GetContext();
   65   if (JSValueIsUndefined(js_ctx, js_val) || JSValueIsNull(js_ctx, js_val)) {
   66     *native_val = Variant(0);
   67     return true;
   68   }
   69 
   70   // Only converts finite number to int.
   71   if (ctx->IsFinite(js_val)) {
   72     JSValueRef exception = NULL;
   73     double result = JSValueToNumber(js_ctx, js_val, &exception);
   74     *native_val = Variant(static_cast<int64_t>(round(result)));
   75     return ctx->CheckJSException(exception);
   76   }
   77   return false;
   78 }
   79 
   80 static bool ConvertJSToNativeDouble(JSScriptContext *ctx, JSValueRef js_val,
   81                                     Variant *native_val) {
   82   JSContextRef js_ctx = ctx->GetContext();
   83   if (JSValueIsUndefined(js_ctx, js_val) || JSValueIsNull(js_ctx, js_val)) {
   84     *native_val = Variant(0.0);
   85     return true;
   86   }
   87 
   88   // If js_val is a number, then any value is allowed.
   89   // Otherwise, anything that can only be converted to NaN is not allowed.
   90   if (JSValueIsNumber(js_ctx, js_val) || !ctx->IsNaN(js_val)) {
   91     JSValueRef exception = NULL;
   92     *native_val = Variant(JSValueToNumber(js_ctx, js_val, &exception));
   93     return ctx->CheckJSException(exception);
   94   }
   95   return false;
   96 }
   97 
   98 static bool ConvertJSToNativeNumber(JSScriptContext *ctx, JSValueRef js_val,
   99                                     Variant *native_val) {
  100   JSContextRef js_ctx = ctx->GetContext();
  101   if (JSValueIsUndefined(js_ctx, js_val) || JSValueIsNull(js_ctx, js_val)) {
  102     *native_val = Variant(0);
  103     return true;
  104   }
  105 
  106   // If js_val is a number, then any value is allowed.
  107   // Otherwise, anything that can only be converted to NaN is not allowed.
  108   if (JSValueIsNumber(js_ctx, js_val) || !ctx->IsNaN(js_val)) {
  109     JSValueRef exception = NULL;
  110     double double_value = JSValueToNumber(js_ctx, js_val, &exception);
  111     // convert to int if possible.
  112     if (ceil(double_value) == floor(double_value) &&
  113         double_value <= LONG_MAX && double_value >= LONG_MIN) {
  114       *native_val = Variant(static_cast<int64_t>(double_value));
  115     } else {
  116       *native_val = Variant(double_value);
  117     }
  118     return ctx->CheckJSException(exception);
  119   }
  120   return false;
  121 }
  122 
  123 static bool ConvertJSToNativeString(JSScriptContext *ctx, JSValueRef js_val,
  124                                     Variant *native_val) {
  125   JSContextRef js_ctx = ctx->GetContext();
  126   if (JSValueIsNull(js_ctx, js_val)) {
  127     *native_val = Variant(static_cast<const char *>(NULL));
  128     return true;
  129   }
  130   if (JSValueIsUndefined(js_ctx, js_val)) {
  131     // Default value of a string is "";
  132     *native_val = Variant("");
  133     return true;
  134   }
  135 
  136   JSValueRef exception = NULL;
  137   if (JSValueIsObject(js_ctx, js_val)) {
  138     // Here allows asssigning ScriptableBinaryData to a native string, because
  139     // Windows version also allows it.
  140     JSObjectRef js_obj = JSValueToObject(js_ctx, js_val, &exception);
  141     if (!js_obj) {
  142       ctx->CheckJSException(exception);
  143       return false;
  144     }
  145     ScriptableInterface *scriptable = ctx->UnwrapScriptable(js_obj);
  146     if (scriptable &&
  147         scriptable->IsInstanceOf(ScriptableBinaryData::CLASS_ID)) {
  148       ScriptableBinaryData *data =
  149           down_cast<ScriptableBinaryData *>(scriptable);
  150       *native_val = Variant(data->data());
  151       DLOG("Convert binary data to string: length=%zu", data->data().size());
  152       return true;
  153     }
  154   }
  155   JSStringRef js_str = JSValueToStringCopy(js_ctx, js_val, &exception);
  156   if (!js_str) {
  157     ctx->CheckJSException(exception);
  158     return false;
  159   }
  160   *native_val = Variant(ConvertJSStringToUTF8(js_str));
  161   JSStringRelease(js_str);
  162   return true;
  163 }
  164 
  165 static bool ConvertJSToNativeUTF16String(JSScriptContext *ctx,
  166                                          JSValueRef js_val,
  167                                          Variant *native_val) {
  168   static const UTF16Char kEmptyUTF16String[] = { 0 };
  169   JSContextRef js_ctx = ctx->GetContext();
  170   if (JSValueIsNull(js_ctx, js_val)) {
  171     *native_val = Variant(static_cast<const UTF16Char *>(NULL));
  172   }
  173   if (JSValueIsUndefined(js_ctx, js_val)) {
  174     *native_val = Variant(kEmptyUTF16String);
  175   }
  176 
  177   JSValueRef exception = NULL;
  178   JSStringRef js_str = JSValueToStringCopy(js_ctx, js_val, &exception);
  179   if (!js_str) {
  180     ctx->CheckJSException(exception);
  181     return false;
  182   }
  183   const JSChar *js_chars = JSStringGetCharactersPtr(js_str);
  184   if (js_chars)
  185     *native_val = Variant(UTF16String(js_chars, JSStringGetLength(js_str)));
  186   JSStringRelease(js_str);
  187   return js_chars != NULL;
  188 }
  189 
  190 static bool ConvertJSToScriptable(JSScriptContext *ctx, JSValueRef js_val,
  191                                   Variant *native_val) {
  192   bool result = true;
  193   ScriptableInterface *scriptable = NULL;
  194   JSValueRef exception = NULL;
  195   JSContextRef js_ctx = ctx->GetContext();
  196   if (JSValueIsUndefined(js_ctx, js_val) || JSValueIsNull(js_ctx, js_val) ||
  197       (JSValueIsNumber(js_ctx, js_val) &&
  198        JSValueToNumber(js_ctx, js_val, &exception) == 0)) {
  199     scriptable = NULL;
  200   } else if (JSValueIsObject(js_ctx, js_val)) {
  201     scriptable = ctx->WrapJSObject(JSValueToObject(js_ctx, js_val, &exception));
  202   } else {
  203     result = false;
  204   }
  205 
  206   if (result && ctx->CheckJSException(exception)) {
  207     *native_val = Variant(scriptable);
  208     return true;
  209   }
  210   return false;
  211 }
  212 
  213 static bool ConvertJSToSlot(JSScriptContext *ctx, JSObjectRef owner,
  214                             const Variant &prototype, JSValueRef js_val,
  215                             Variant *native_val) {
  216   bool result = true;
  217   JSObjectRef func_obj = NULL;
  218   JSValueRef exception = NULL;
  219   JSContextRef js_ctx = ctx->GetContext();
  220   if (JSValueIsNull(js_ctx, js_val) || JSValueIsUndefined(js_ctx, js_val) ||
  221       (JSValueIsNumber(js_ctx, js_val) &&
  222        JSValueToNumber(js_ctx, js_val, &exception) == 0)) {
  223     func_obj = NULL;
  224   } else if (JSValueIsString(js_ctx, js_val)) {
  225     JSStringRef js_body = JSValueToStringCopy(js_ctx, js_val, &exception);
  226     std::string body = ConvertJSStringToUTF8(js_body);
  227     JSStringRelease(js_body);
  228     std::string filename;
  229     int lineno;
  230     ctx->GetCurrentFileAndLine(&filename, &lineno);
  231     func_obj = CompileFunction(ctx, body.c_str(), filename.c_str(), lineno,
  232                                &exception);
  233   } else if (JSValueIsObject(js_ctx, js_val)) {
  234     func_obj = JSValueToObject(js_ctx, js_val, &exception);
  235     if (!func_obj || !JSObjectIsFunction(js_ctx, func_obj))
  236       result = false;
  237   }
  238 
  239   if (result && ctx->CheckJSException(exception)) {
  240     Slot *slot = NULL;
  241     if (func_obj) {
  242       slot = ctx->WrapJSObjectIntoSlot(VariantValue<Slot *>()(prototype),
  243                                        owner, func_obj);
  244     }
  245     *native_val = Variant(slot);
  246     return true;
  247   }
  248   return false;
  249 }
  250 
  251 static bool ConvertJSToNativeDate(JSScriptContext *ctx, JSValueRef js_val,
  252                                   Variant *native_val) {
  253   JSContextRef js_ctx = ctx->GetContext();
  254   if (JSValueIsUndefined(js_ctx, js_val) || JSValueIsNull(js_ctx, js_val)) {
  255     // Special rule to keep compatibile with Windows version.
  256     *native_val = Variant(Date(0));
  257     return true;
  258   }
  259 
  260   if (JSValueIsObject(js_ctx, js_val)) {
  261     std::string time_string;
  262     if (DateGetTimeString(ctx, js_val, &time_string)) {
  263 #if GGL_SIZEOF_LONG_INT >= 8
  264       *native_val = Variant(
  265           Date(static_cast<uint64_t>(strtol(time_string.c_str(), NULL, 10))));
  266 #else
  267       *native_val = Variant(
  268           Date(static_cast<uint64_t>(strtoll(time_string.c_str(), NULL, 10))));
  269 #endif
  270       return true;
  271     }
  272     return false;
  273   }
  274 
  275   Variant int_val(0);
  276   if (ConvertJSToNativeInt(ctx, js_val, &int_val)) {
  277     *native_val = Variant(Date(VariantValue<uint64_t>()(int_val)));
  278     return true;
  279   }
  280   return false;
  281 }
  282 
  283 static bool ConvertJSToJSON(JSScriptContext *ctx, JSValueRef js_val,
  284                             Variant *native_val) {
  285   std::string json;
  286   if (JSONEncode(ctx, js_val, &json)) {
  287     *native_val = Variant(JSONString(json));
  288     return true;
  289   }
  290   return false;
  291 }
  292 
  293 bool ConvertJSToNativeVariant(JSScriptContext *ctx, JSValueRef js_val,
  294                               Variant *native_val) {
  295   JSContextRef js_ctx = ctx->GetContext();
  296   switch(JSValueGetType(js_ctx, js_val)) {
  297     case kJSTypeUndefined:
  298     case kJSTypeNull:
  299       return ConvertJSToNativeVoid(ctx, js_val, native_val);
  300     case kJSTypeBoolean:
  301       return ConvertJSToNativeBool(ctx, js_val, native_val);
  302     case kJSTypeNumber:
  303       return ConvertJSToNativeNumber(ctx, js_val, native_val);
  304     case kJSTypeString:
  305       return ConvertJSToNativeString(ctx, js_val, native_val);
  306     case kJSTypeObject:
  307       // Don't try to convert the object to native Date, because JavaScript Date
  308       // is mutable, and sometimes the script may want to read it back and
  309       // change it. We only convert to native Date if the native side explicitly
  310       // requires a Date.
  311       // JS function is also wrapped into Scriptable instead of being
  312       // converted to a Slot, to ease memory management.
  313       return ConvertJSToScriptable(ctx, js_val, native_val);
  314     default:
  315       return false;
  316   }
  317 }
  318 
  319 bool ConvertJSToNative(JSScriptContext *ctx, JSObjectRef owner,
  320                        const Variant &prototype,
  321                        JSValueRef js_val, Variant *native_val) {
  322   switch (prototype.type()) {
  323     case Variant::TYPE_VOID:
  324       return ConvertJSToNativeVoid(ctx, js_val, native_val);
  325     case Variant::TYPE_BOOL:
  326       return ConvertJSToNativeBool(ctx, js_val, native_val);
  327     case Variant::TYPE_INT64:
  328       return ConvertJSToNativeInt(ctx, js_val, native_val);
  329     case Variant::TYPE_DOUBLE:
  330       return ConvertJSToNativeDouble(ctx, js_val, native_val);
  331     case Variant::TYPE_STRING:
  332       return ConvertJSToNativeString(ctx, js_val, native_val);
  333     case Variant::TYPE_JSON:
  334       return ConvertJSToJSON(ctx, js_val, native_val);
  335     case Variant::TYPE_UTF16STRING:
  336       return ConvertJSToNativeUTF16String(ctx, js_val, native_val);
  337     case Variant::TYPE_SCRIPTABLE:
  338       return ConvertJSToScriptable(ctx, js_val, native_val);
  339     case Variant::TYPE_SLOT:
  340       return ConvertJSToSlot(ctx, owner, prototype, js_val, native_val);
  341     case Variant::TYPE_DATE:
  342       return ConvertJSToNativeDate(ctx, js_val, native_val);
  343     case Variant::TYPE_ANY:
  344     case Variant::TYPE_CONST_ANY:
  345       return false;
  346     case Variant::TYPE_VARIANT:
  347       return ConvertJSToNativeVariant(ctx, js_val, native_val);
  348     default:
  349       return false;
  350   }
  351 }
  352 
  353 void FreeNativeValue(const Variant &native_val) {
  354   // Delete the JSFunctionSlot object that was created by ConvertJSToNative().
  355   if (native_val.type() == Variant::TYPE_SLOT)
  356     delete VariantValue<Slot *>()(native_val);
  357 }
  358 
  359 std::string PrintJSValue(JSScriptContext *ctx, JSValueRef js_val) {
  360   JSContextRef js_ctx = ctx->GetContext();
  361   switch(JSValueGetType(js_ctx, js_val)) {
  362     case kJSTypeString: {
  363       Variant v;
  364       ConvertJSToNativeString(ctx, js_val, &v);
  365       return VariantValue<std::string>()(v);
  366     }
  367     case kJSTypeObject: {
  368       std::string json;
  369       JSONEncode(ctx, js_val, &json);
  370       return json;
  371     }
  372     default: {
  373       JSStringRef js_str = JSValueToStringCopy(js_ctx, js_val, NULL);
  374       if (js_str) {
  375         std::string utf8 = ConvertJSStringToUTF8(js_str);
  376         JSStringRelease(js_str);
  377         return utf8;
  378       }
  379       return "##ERROR##";
  380     }
  381   }
  382 }
  383 
  384 bool ConvertJSArgsToNative(JSScriptContext *ctx, JSObjectRef owner,
  385                            const char *name, Slot *slot,
  386                            size_t argc, const JSValueRef argv[],
  387                            Variant **params, size_t *expected_argc,
  388                            JSValueRef *exception) {
  389   *params = NULL;
  390   const Variant::Type *arg_types = NULL;
  391   *expected_argc = argc;
  392   const Variant *default_args = NULL;
  393   if (slot->HasMetadata()) {
  394     arg_types = slot->GetArgTypes();
  395     *expected_argc = static_cast<size_t>(slot->GetArgCount());
  396     if (*expected_argc == INT_MAX) {
  397       // Simply converts each arguments to native.
  398       *params = new Variant[argc];
  399       *expected_argc = argc;
  400       size_t arg_type_idx = 0;
  401       for (size_t i = 0; i < argc; ++i) {
  402         bool result = false;
  403         if (arg_types && arg_types[arg_type_idx] != Variant::TYPE_VOID) {
  404           result = ConvertJSToNative(ctx, owner,
  405                                      Variant(arg_types[arg_type_idx]),
  406                                      argv[i], &(*params)[i]);
  407           ++arg_type_idx;
  408         } else {
  409           result = ConvertJSToNativeVariant(ctx, argv[i], &(*params)[i]);
  410         }
  411         if (!result) {
  412           for (size_t j = 0; j < i; ++j)
  413             FreeNativeValue((*params)[j]);
  414           delete [] *params;
  415           *params = NULL;
  416           RaiseJSException(ctx, exception,
  417                            "Failed to convert argument %zu (%s) of function(%s)"
  418                            " to native.", i, PrintJSValue(ctx, argv[i]).c_str(),
  419                            name);
  420           return false;
  421         }
  422       }
  423       return true;
  424     }
  425     default_args = slot->GetDefaultArgs();
  426     if (argc != *expected_argc) {
  427       size_t min_argc = *expected_argc;
  428       if (min_argc > 0 && default_args && argc < *expected_argc) {
  429         for (int i = static_cast<int>(min_argc) - 1; i >= 0; i--) {
  430           if (default_args[i].type() != Variant::TYPE_VOID)
  431             min_argc--;
  432           else
  433             break;
  434         }
  435       }
  436 
  437       if (argc > *expected_argc || argc < min_argc) {
  438         // Argc mismatch.
  439         RaiseJSException(ctx, exception,
  440                          "Wrong number of arguments for function(%s): %zu "
  441                          "(expected: %zu, at least: %zu)",
  442                          name, argc, *expected_argc, min_argc);
  443         return false;
  444       }
  445     }
  446   }
  447 
  448   if (*expected_argc > 0) {
  449     *params = new Variant[*expected_argc];
  450     // Fill up trailing default argument values.
  451     for (size_t i = argc; i < *expected_argc; ++i) {
  452       ASSERT(default_args);  // Otherwise already returned JS_FALSE.
  453       (*params)[i] = default_args[i];
  454     }
  455 
  456     JSContextRef js_ctx = ctx->GetContext();
  457     for (size_t i = 0; i < argc; ++i) {
  458       if (default_args && default_args[i].type() != Variant::TYPE_VOID &&
  459           JSValueIsUndefined(js_ctx, argv[i])) {
  460         // Use the default value.
  461         (*params)[i] = default_args[i];
  462       } else {
  463         bool result;
  464         if (arg_types) {
  465           result = ConvertJSToNative(ctx, owner,
  466                                      Variant(arg_types[i]), argv[i],
  467                                      &(*params)[i]);
  468         } else {
  469           result = ConvertJSToNativeVariant(ctx, argv[i], &(*params)[i]);
  470         }
  471         if (!result) {
  472           for (size_t j = 0; j < i; ++j)
  473             FreeNativeValue((*params)[j]);
  474           delete [] *params;
  475           *params = NULL;
  476           RaiseJSException(ctx, exception,
  477                            "Failed to convert argument %zu (%s) of function(%s)"
  478                            " to native.", i, PrintJSValue(ctx, argv[i]).c_str(),
  479                            name);
  480           return false;
  481         }
  482       }
  483     }
  484   }
  485   return true;
  486 }
  487 
  488 static bool ConvertNativeToJSVoid(JSScriptContext *ctx,
  489                                   const Variant &native_val,
  490                                   JSValueRef *js_val) {
  491   *js_val = JSValueMakeUndefined(ctx->GetContext());
  492   return true;
  493 }
  494 
  495 static bool ConvertNativeToJSBool(JSScriptContext *ctx,
  496                                   const Variant &native_val,
  497                                   JSValueRef *js_val) {
  498   *js_val = JSValueMakeBoolean(ctx->GetContext(),
  499                                VariantValue<bool>()(native_val));
  500   return true;
  501 }
  502 
  503 static bool ConvertNativeToJSInt(JSScriptContext *ctx,
  504                                  const Variant &native_val,
  505                                  JSValueRef *js_val) {
  506   int64_t value = VariantValue<int64_t>()(native_val);
  507   *js_val = JSValueMakeNumber(ctx->GetContext(), static_cast<double>(value));
  508   return true;
  509 }
  510 
  511 static bool ConvertNativeToJSDouble(JSScriptContext *ctx,
  512                                     const Variant &native_val,
  513                                     JSValueRef *js_val) {
  514   *js_val = JSValueMakeNumber(ctx->GetContext(),
  515                               VariantValue<double>()(native_val));
  516   return true;
  517 }
  518 
  519 static bool ConvertNativeToJSString(JSScriptContext *ctx,
  520                                     const Variant &native_val,
  521                                     JSValueRef *js_val) {
  522   JSContextRef js_ctx = ctx->GetContext();
  523   if (!VariantValue<const char *>()(native_val)) {
  524     *js_val = JSValueMakeNull(js_ctx);
  525   } else {
  526     std::string src = VariantValue<std::string>()(native_val);
  527     size_t src_len = src.length();
  528     UTF16String dest;
  529     if (ConvertStringUTF8ToUTF16(src, &dest) != src_len) {
  530       DLOG("Convert non-UTF8 string data to fake UTF16, length=%zu", src_len);
  531       size_t dest_len = (src_len + 1) / 2;
  532       dest.clear();
  533       dest.reserve(dest_len);
  534       const char *src_ptr = src.c_str();
  535       // Failed to convert to utf16, the source may contain arbitrary binary
  536       // data. This is mainly for compatibility of XMLHttpRequest.responseBody
  537       // to Microsoft, that combines each two bytes into one 16 bit word.
  538       for (size_t i = 0; i < src_len; i += 2) {
  539         dest += static_cast<UTF16Char>(
  540             static_cast<unsigned char>(src_ptr[i]) |
  541             (static_cast<unsigned char>(src_ptr[i + 1]) << 8));
  542       }
  543       ASSERT(dest.length() == dest_len);
  544     }
  545 
  546     JSStringRef js_str = JSStringCreateWithCharacters(
  547         static_cast<const JSChar *>(dest.c_str()), dest.length());
  548     *js_val = JSValueMakeString(js_ctx, js_str);
  549     JSStringRelease(js_str);
  550   }
  551   return true;
  552 }
  553 
  554 static bool ConvertNativeUTF16ToJSString(JSScriptContext *ctx,
  555                                          const Variant &native_val,
  556                                          JSValueRef *js_val) {
  557   const UTF16Char *ptr = VariantValue<const UTF16Char *>()(native_val);
  558   JSContextRef js_ctx = ctx->GetContext();
  559   if (!ptr) {
  560     *js_val = JSValueMakeNull(js_ctx);
  561   } else {
  562     size_t len = VariantValue<const UTF16String &>()(native_val).length();
  563     JSStringRef js_str =
  564         JSStringCreateWithCharacters(static_cast<const JSChar *>(ptr), len);
  565     *js_val = JSValueMakeString(js_ctx, js_str);
  566     JSStringRelease(js_str);
  567   }
  568   return true;
  569 }
  570 
  571 static JSValueRef ReturnSelf(JSContextRef ctx, JSObjectRef function,
  572                              JSObjectRef thisObject, size_t argc,
  573                              const JSValueRef argv[], JSValueRef *exception) {
  574   return thisObject;
  575 }
  576 
  577 static JSValueRef GetCollectionItem(JSContextRef ctx, JSObjectRef function,
  578                                     JSObjectRef thisObject, size_t argc,
  579                                     const JSValueRef argv[],
  580                                     JSValueRef *exception) {
  581   if (argc >= 1) {
  582     JSValueRef local_exception = NULL;
  583     double idx = JSValueToNumber(ctx, argv[0], &local_exception);
  584     if (!local_exception) {
  585       return JSObjectGetPropertyAtIndex(ctx, thisObject,
  586                                         static_cast<unsigned>(idx), exception);
  587     }
  588     *exception = local_exception;
  589   }
  590   return JSValueMakeUndefined(ctx);
  591 }
  592 
  593 static JSObjectRef CreateJSArray(JSContextRef ctx) {
  594   JSStringRef array_class_name = JSStringCreateWithUTF8CString("Array");
  595   JSObjectRef global_object = JSContextGetGlobalObject(ctx);
  596   JSValueRef array_class = JSObjectGetProperty(ctx, global_object,
  597                                                array_class_name, NULL);
  598   JSStringRelease(array_class_name);
  599   if (JSValueIsObject(ctx, array_class)) {
  600     JSObjectRef array_class_obj = JSValueToObject(ctx, array_class, NULL);
  601     if (JSObjectIsConstructor(ctx, array_class_obj)) {
  602       JSValueRef array = JSObjectCallAsFunction(ctx, array_class_obj, NULL,
  603                                                 0, NULL, NULL);
  604       if (JSValueIsInstanceOfConstructor(ctx, array, array_class_obj, NULL)) {
  605         return JSValueToObject(ctx, array, NULL);
  606       }
  607     }
  608   }
  609   return NULL;
  610 }
  611 
  612 static JSObjectRef CustomizeJSArray(JSContextRef ctx, JSObjectRef array) {
  613   if (array) {
  614     JSStringRef toArray_str = JSStringCreateWithUTF8CString("toArray");
  615     JSObjectRef toArray =
  616         JSObjectMakeFunctionWithCallback(ctx, NULL, ReturnSelf);
  617     JSObjectSetProperty(ctx, array, toArray_str, toArray,
  618                         kJSPropertyAttributeDontEnum, NULL);
  619     JSStringRelease(toArray_str);
  620 
  621     JSStringRef item_str = JSStringCreateWithUTF8CString("item");
  622     JSObjectRef item =
  623         JSObjectMakeFunctionWithCallback(ctx, NULL, GetCollectionItem);
  624     JSObjectSetProperty(ctx, array, item_str, item,
  625                         kJSPropertyAttributeDontEnum, NULL);
  626     JSStringRelease(item_str);
  627 
  628     JSStringRef count_str = JSStringCreateWithUTF8CString("count");
  629     JSStringRef length_str = JSStringCreateWithUTF8CString("length");
  630     JSValueRef length = JSObjectGetProperty(ctx, array, length_str, NULL);
  631     JSObjectSetProperty(ctx, array, count_str, length,
  632                         kJSPropertyAttributeReadOnly |
  633                         kJSPropertyAttributeDontEnum, NULL);
  634     JSStringRelease(count_str);
  635     JSStringRelease(length_str);
  636   }
  637   return array;
  638 }
  639 
  640 static bool ConvertNativeArrayToJS(JSScriptContext *ctx,
  641                                    ScriptableArray *array,
  642                                    JSValueRef *js_val) {
  643   // To make sure that the array will be destroyed correctly.
  644   ScriptableHolder<ScriptableArray> array_holder(array);
  645   size_t length = array->GetCount();
  646   if (length > INT_MAX)
  647     return false;
  648 
  649   JSContextRef js_ctx = ctx->GetContext();
  650   JSObjectRef js_array = CreateJSArray(js_ctx);
  651   if (!js_array)
  652     return false;
  653 
  654   for (size_t i = 0; i < length; ++i) {
  655     JSValueRef item;
  656     if (ConvertNativeToJS(ctx, array->GetItem(i), &item)) {
  657       JSValueRef exception = NULL;
  658       JSObjectSetPropertyAtIndex(js_ctx, js_array, static_cast<unsigned>(i),
  659                                  item, &exception);
  660       if (exception)
  661         ctx->CheckJSException(exception);
  662     }
  663   }
  664 
  665   *js_val = CustomizeJSArray(js_ctx, js_array);
  666   return true;
  667 }
  668 
  669 static bool ConvertNativeToJSObject(JSScriptContext *ctx,
  670                                     const Variant &native_val,
  671                                     JSValueRef *js_val) {
  672   bool result = true;
  673   ScriptableInterface *scriptable =
  674       VariantValue<ScriptableInterface *>()(native_val);
  675   if (!scriptable) {
  676     *js_val = JSValueMakeNull(ctx->GetContext());
  677   } else if (scriptable->IsInstanceOf(ScriptableArray::CLASS_ID)) {
  678     result = ConvertNativeArrayToJS(
  679         ctx, down_cast<ScriptableArray *>(scriptable), js_val);
  680   } else {
  681     // If scriptable is a JSScriptableWrapper of this context, then just unwrap
  682     // it.
  683     *js_val = ctx->UnwrapJSObject(scriptable);
  684     // If failed to unwrap it, then wrap it into a JSObjectRef.
  685     if (!*js_val)
  686       *js_val = ctx->WrapScriptable(scriptable);
  687   }
  688   return result;
  689 }
  690 
  691 static bool ConvertNativeToJSDate(JSScriptContext *ctx,
  692                                   const Variant &native_val,
  693                                   JSValueRef *js_val) {
  694   std::string new_date_script =
  695       StringPrintf("new Date(%ju)", VariantValue<Date>()(native_val).value);
  696   JSStringRef js_script =
  697       JSStringCreateWithUTF8CString(new_date_script.c_str());
  698   JSValueRef exception = NULL;
  699   *js_val = JSEvaluateScript(ctx->GetContext(), js_script,
  700                              NULL, NULL, 0, &exception);
  701   return ctx->CheckJSException(exception);
  702 }
  703 
  704 static bool ConvertNativeToJSFunction(JSScriptContext *ctx,
  705                                       const Variant &native_val,
  706                                       JSValueRef *js_val) {
  707   return ctx->UnwrapJSFunctionSlot(VariantValue<Slot *>()(native_val), js_val);
  708 }
  709 
  710 static bool ConvertJSONToJS(JSScriptContext *ctx,
  711                             const Variant &native_val,
  712                             JSValueRef *js_val) {
  713   JSONString json_str = VariantValue<JSONString>()(native_val);
  714   return JSONDecode(ctx, json_str.value.c_str(), js_val);
  715 }
  716 
  717 bool ConvertNativeToJS(JSScriptContext *ctx, const Variant &native_val,
  718                        JSValueRef *js_val) {
  719   switch (native_val.type()) {
  720     case Variant::TYPE_VOID:
  721       return ConvertNativeToJSVoid(ctx, native_val, js_val);
  722     case Variant::TYPE_BOOL:
  723       return ConvertNativeToJSBool(ctx, native_val, js_val);
  724     case Variant::TYPE_INT64:
  725       return ConvertNativeToJSInt(ctx, native_val, js_val);
  726     case Variant::TYPE_DOUBLE:
  727       return ConvertNativeToJSDouble(ctx, native_val, js_val);
  728     case Variant::TYPE_STRING:
  729       return ConvertNativeToJSString(ctx, native_val, js_val);
  730     case Variant::TYPE_JSON:
  731       return ConvertJSONToJS(ctx, native_val, js_val);
  732     case Variant::TYPE_UTF16STRING:
  733       return ConvertNativeUTF16ToJSString(ctx, native_val, js_val);
  734     case Variant::TYPE_SCRIPTABLE:
  735       return ConvertNativeToJSObject(ctx, native_val, js_val);
  736     case Variant::TYPE_SLOT:
  737       return ConvertNativeToJSFunction(ctx, native_val, js_val);
  738     case Variant::TYPE_DATE:
  739       return ConvertNativeToJSDate(ctx, native_val, js_val);
  740     case Variant::TYPE_ANY:
  741     case Variant::TYPE_CONST_ANY:
  742       return false;
  743     case Variant::TYPE_VARIANT:
  744       // Normally there is no real value of this type, so convert it to void.
  745       return ConvertNativeToJSVoid(ctx, native_val, js_val);
  746     default:
  747       return false;
  748   }
  749 }
  750 
  751 JSObjectRef CompileFunction(JSScriptContext *ctx, const char *script,
  752                             const char *filename, int lineno,
  753                             JSValueRef *exception) {
  754   if (!script)
  755     return NULL;
  756 
  757   std::string massaged_script = MassageJScript(script, false, filename, lineno);
  758   JSStringRef js_script =
  759       JSStringCreateWithUTF8CString(massaged_script.c_str());
  760   JSStringRef src_url =
  761       (filename && *filename) ? JSStringCreateWithUTF8CString(filename) : NULL;
  762   JSObjectRef result = JSObjectMakeFunction(
  763       ctx->GetContext(), NULL, 0, NULL, js_script, src_url, lineno, exception);
  764   JSStringRelease(js_script);
  765   if (src_url)
  766     JSStringRelease(src_url);
  767   return result;
  768 }
  769 
  770 void RaiseJSException(JSScriptContext *ctx, JSValueRef *exception,
  771                       const char *format, ...) {
  772   if(exception && !*exception) {
  773     va_list ap;
  774     va_start(ap, format);
  775     std::string message = StringVPrintf(format, ap);
  776     va_end(ap);
  777     JSStringRef js_str = JSStringCreateWithUTF8CString(message.c_str());
  778     *exception = JSValueMakeString(ctx->GetContext(), js_str);
  779     JSStringRelease(js_str);
  780   }
  781 }
  782 
  783 } // namespace webkit
  784 } // namespace ggadget