1// Copyright 2014 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 "extensions/renderer/set_icon_natives.h"
6
7#include <limits>
8
9#include "base/memory/scoped_ptr.h"
10#include "content/public/common/common_param_traits.h"
11#include "extensions/renderer/request_sender.h"
12#include "extensions/renderer/script_context.h"
13#include "ipc/ipc_message_utils.h"
14#include "third_party/WebKit/public/web/WebArrayBufferConverter.h"
15#include "third_party/skia/include/core/SkBitmap.h"
16#include "ui/gfx/ipc/gfx_param_traits.h"
17
18namespace {
19
20const char* kImageSizeKeys[] = {"19", "38"};
21const char kInvalidDimensions[] = "ImageData has invalid dimensions.";
22const char kInvalidData[] = "ImageData data length does not match dimensions.";
23const char kNoMemory[] = "Chrome was unable to initialize icon.";
24
25}  // namespace
26
27namespace extensions {
28
29SetIconNatives::SetIconNatives(RequestSender* request_sender,
30                               ScriptContext* context)
31    : ObjectBackedNativeHandler(context), request_sender_(request_sender) {
32  RouteFunction(
33      "SetIconCommon",
34      base::Bind(&SetIconNatives::SetIconCommon, base::Unretained(this)));
35}
36
37bool SetIconNatives::ConvertImageDataToBitmapValue(
38    const v8::Local<v8::Object> image_data,
39    v8::Local<v8::Value>* image_data_bitmap) {
40  v8::Isolate* isolate = context()->v8_context()->GetIsolate();
41  v8::Local<v8::Object> data =
42      image_data->Get(v8::String::NewFromUtf8(isolate, "data"))->ToObject();
43  int width =
44      image_data->Get(v8::String::NewFromUtf8(isolate, "width"))->Int32Value();
45  int height =
46      image_data->Get(v8::String::NewFromUtf8(isolate, "height"))->Int32Value();
47
48  if (width <= 0 || height <= 0) {
49    isolate->ThrowException(v8::Exception::Error(
50        v8::String::NewFromUtf8(isolate, kInvalidDimensions)));
51    return false;
52  }
53
54  // We need to be able to safely check |data_length| == 4 * width * height
55  // without overflowing below.
56  int max_width = (std::numeric_limits<int>::max() / 4) / height;
57  if (width > max_width) {
58    isolate->ThrowException(v8::Exception::Error(
59        v8::String::NewFromUtf8(isolate, kInvalidDimensions)));
60    return false;
61  }
62
63  int data_length =
64      data->Get(v8::String::NewFromUtf8(isolate, "length"))->Int32Value();
65  if (data_length != 4 * width * height) {
66    isolate->ThrowException(
67        v8::Exception::Error(v8::String::NewFromUtf8(isolate, kInvalidData)));
68    return false;
69  }
70
71  SkBitmap bitmap;
72  if (!bitmap.tryAllocN32Pixels(width, height)) {
73    isolate->ThrowException(
74        v8::Exception::Error(v8::String::NewFromUtf8(isolate, kNoMemory)));
75    return false;
76  }
77  bitmap.eraseARGB(0, 0, 0, 0);
78
79  uint32_t* pixels = bitmap.getAddr32(0, 0);
80  for (int t = 0; t < width * height; t++) {
81    // |data| is RGBA, pixels is ARGB.
82    pixels[t] = SkPreMultiplyColor(
83        ((data->Get(v8::Integer::New(isolate, 4 * t + 3))->Int32Value() & 0xFF)
84         << 24) |
85        ((data->Get(v8::Integer::New(isolate, 4 * t + 0))->Int32Value() & 0xFF)
86         << 16) |
87        ((data->Get(v8::Integer::New(isolate, 4 * t + 1))->Int32Value() & 0xFF)
88         << 8) |
89        ((data->Get(v8::Integer::New(isolate, 4 * t + 2))->Int32Value() & 0xFF)
90         << 0));
91  }
92
93  // Construct the Value object.
94  IPC::Message bitmap_pickle;
95  IPC::WriteParam(&bitmap_pickle, bitmap);
96  blink::WebArrayBuffer buffer =
97      blink::WebArrayBuffer::create(bitmap_pickle.size(), 1);
98  memcpy(buffer.data(), bitmap_pickle.data(), bitmap_pickle.size());
99  *image_data_bitmap = blink::WebArrayBufferConverter::toV8Value(
100      &buffer, context()->v8_context()->Global(), isolate);
101
102  return true;
103}
104
105bool SetIconNatives::ConvertImageDataSetToBitmapValueSet(
106    v8::Local<v8::Object>& details,
107    v8::Local<v8::Object>* bitmap_set_value) {
108  v8::Isolate* isolate = context()->v8_context()->GetIsolate();
109  v8::Local<v8::Object> image_data_set =
110      details->Get(v8::String::NewFromUtf8(isolate, "imageData"))->ToObject();
111
112  DCHECK(bitmap_set_value);
113  for (size_t i = 0; i < arraysize(kImageSizeKeys); i++) {
114    if (!image_data_set->Has(
115            v8::String::NewFromUtf8(isolate, kImageSizeKeys[i])))
116      continue;
117    v8::Local<v8::Object> image_data =
118        image_data_set->Get(v8::String::NewFromUtf8(isolate, kImageSizeKeys[i]))
119            ->ToObject();
120    v8::Local<v8::Value> image_data_bitmap;
121    if (!ConvertImageDataToBitmapValue(image_data, &image_data_bitmap))
122      return false;
123    (*bitmap_set_value)->Set(
124        v8::String::NewFromUtf8(isolate, kImageSizeKeys[i]), image_data_bitmap);
125  }
126  return true;
127}
128
129void SetIconNatives::SetIconCommon(
130    const v8::FunctionCallbackInfo<v8::Value>& args) {
131  CHECK_EQ(1, args.Length());
132  CHECK(args[0]->IsObject());
133  v8::Local<v8::Object> details = args[0]->ToObject();
134  v8::Local<v8::Object> bitmap_set_value(v8::Object::New(args.GetIsolate()));
135  if (!ConvertImageDataSetToBitmapValueSet(details, &bitmap_set_value))
136    return;
137
138  v8::Local<v8::Object> dict(v8::Object::New(args.GetIsolate()));
139  dict->Set(v8::String::NewFromUtf8(args.GetIsolate(), "imageData"),
140            bitmap_set_value);
141  if (details->Has(v8::String::NewFromUtf8(args.GetIsolate(), "tabId"))) {
142    dict->Set(
143        v8::String::NewFromUtf8(args.GetIsolate(), "tabId"),
144        details->Get(v8::String::NewFromUtf8(args.GetIsolate(), "tabId")));
145  }
146  args.GetReturnValue().Set(dict);
147}
148
149}  // namespace extensions
150