| OLD | NEW |
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "ppapi/proxy/ppb_font_proxy.h" | 5 #include "ppapi/proxy/ppb_font_proxy.h" |
| 6 | 6 |
| 7 #include "base/bind.h" |
| 7 #include "ppapi/c/dev/ppb_font_dev.h" | 8 #include "ppapi/c/dev/ppb_font_dev.h" |
| 8 #include "ppapi/proxy/plugin_dispatcher.h" | 9 #include "ppapi/proxy/plugin_dispatcher.h" |
| 9 #include "ppapi/proxy/plugin_resource.h" | |
| 10 #include "ppapi/proxy/ppapi_messages.h" | 10 #include "ppapi/proxy/ppapi_messages.h" |
| 11 #include "ppapi/proxy/ppb_image_data_proxy.h" |
| 12 #include "ppapi/shared_impl/resource_object_base.h" |
| 13 #include "ppapi/thunk/ppb_image_data_api.h" |
| 14 #include "ppapi/thunk/thunk.h" |
| 15 |
| 16 using ppapi::thunk::PPB_ImageData_API; |
| 17 using pp::shared_impl::WebKitForwarding; |
| 11 | 18 |
| 12 namespace pp { | 19 namespace pp { |
| 13 namespace proxy { | 20 namespace proxy { |
| 14 | 21 |
| 15 class Font : public PluginResource { | |
| 16 public: | |
| 17 Font(const HostResource& resource); | |
| 18 virtual ~Font(); | |
| 19 | |
| 20 // PluginResource overrides. | |
| 21 virtual Font* AsFont() { return this; } | |
| 22 | |
| 23 PP_FontDescription_Dev& desc() { return desc_; } | |
| 24 PP_FontDescription_Dev* desc_ptr() { return &desc_; } | |
| 25 PP_FontMetrics_Dev& metrics() { return metrics_; } | |
| 26 | |
| 27 private: | |
| 28 PP_FontDescription_Dev desc_; | |
| 29 PP_FontMetrics_Dev metrics_; | |
| 30 | |
| 31 DISALLOW_COPY_AND_ASSIGN(Font); | |
| 32 }; | |
| 33 | |
| 34 Font::Font(const HostResource& resource) : PluginResource(resource) { | |
| 35 memset(&desc_, 0, sizeof(PP_FontDescription_Dev)); | |
| 36 desc_.face.type = PP_VARTYPE_UNDEFINED; | |
| 37 memset(&metrics_, 0, sizeof(PP_FontMetrics_Dev)); | |
| 38 } | |
| 39 | |
| 40 Font::~Font() { | |
| 41 PluginVarTracker::GetInstance()->Release(desc_.face); | |
| 42 } | |
| 43 | |
| 44 namespace { | 22 namespace { |
| 45 | 23 |
| 46 PP_Resource Create(PP_Instance instance, | 24 bool PPTextRunToTextRun(const PP_TextRun_Dev* run, |
| 47 const PP_FontDescription_Dev* description) { | 25 WebKitForwarding::Font::TextRun* output) { |
| 48 PluginDispatcher* dispatcher = PluginDispatcher::GetForInstance(instance); | 26 const std::string* str = PluginVarTracker::GetInstance()->GetExistingString( |
| 49 if (!dispatcher) | 27 run->text); |
| 50 return 0; | 28 if (!str) |
| 29 return false; |
| 51 | 30 |
| 52 SerializedFontDescription in_description; | 31 output->text = *str; |
| 53 in_description.SetFromPPFontDescription(dispatcher, *description, true); | 32 output->rtl = PPBoolToBool(run->rtl); |
| 54 | 33 output->override_direction = PPBoolToBool(run->override_direction); |
| 55 HostResource result; | 34 return true; |
| 56 SerializedFontDescription out_description; | |
| 57 std::string out_metrics; | |
| 58 dispatcher->Send(new PpapiHostMsg_PPBFont_Create( | |
| 59 INTERFACE_ID_PPB_FONT, | |
| 60 instance, in_description, &result, &out_description, &out_metrics)); | |
| 61 | |
| 62 if (result.is_null()) | |
| 63 return 0; // Failure creating font. | |
| 64 | |
| 65 linked_ptr<Font> object(new Font(result)); | |
| 66 out_description.SetToPPFontDescription(dispatcher, object->desc_ptr(), true); | |
| 67 | |
| 68 // Convert the metrics, this is just serialized as a string of bytes. | |
| 69 if (out_metrics.size() != sizeof(PP_FontMetrics_Dev)) | |
| 70 return 0; | |
| 71 memcpy(&object->metrics(), out_metrics.data(), sizeof(PP_FontMetrics_Dev)); | |
| 72 | |
| 73 return PluginResourceTracker::GetInstance()->AddResource(object); | |
| 74 } | 35 } |
| 75 | 36 |
| 76 PP_Bool IsFont(PP_Resource resource) { | |
| 77 Font* object = PluginResource::GetAs<Font>(resource); | |
| 78 return BoolToPPBool(!!object); | |
| 79 } | |
| 80 | |
| 81 PP_Bool Describe(PP_Resource font_id, | |
| 82 PP_FontDescription_Dev* description, | |
| 83 PP_FontMetrics_Dev* metrics) { | |
| 84 Font* object = PluginResource::GetAs<Font>(font_id); | |
| 85 if (!object) | |
| 86 return PP_FALSE; | |
| 87 | |
| 88 // Copy the description, the caller expects its face PP_Var to have a ref | |
| 89 // added to it on its behalf. | |
| 90 memcpy(description, &object->desc(), sizeof(PP_FontDescription_Dev)); | |
| 91 PluginVarTracker::GetInstance()->AddRef(description->face); | |
| 92 | |
| 93 memcpy(metrics, &object->metrics(), sizeof(PP_FontMetrics_Dev)); | |
| 94 return PP_TRUE; | |
| 95 } | |
| 96 | |
| 97 PP_Bool DrawTextAt(PP_Resource font_id, | |
| 98 PP_Resource image_data, | |
| 99 const PP_TextRun_Dev* text, | |
| 100 const PP_Point* position, | |
| 101 uint32_t color, | |
| 102 const PP_Rect* clip, | |
| 103 PP_Bool image_data_is_opaque) { | |
| 104 Font* font_object = PluginResource::GetAs<Font>(font_id); | |
| 105 if (!font_object) | |
| 106 return PP_FALSE; | |
| 107 PluginResource* image_object = PluginResourceTracker::GetInstance()-> | |
| 108 GetResourceObject(image_data); | |
| 109 if (!image_object) | |
| 110 return PP_FALSE; | |
| 111 if (font_object->instance() != image_object->instance()) | |
| 112 return PP_FALSE; | |
| 113 | |
| 114 PPBFont_DrawTextAt_Params params; | |
| 115 params.font = font_object->host_resource(); | |
| 116 params.image_data = image_object->host_resource(); | |
| 117 params.text_is_rtl = text->rtl; | |
| 118 params.override_direction = text->override_direction; | |
| 119 params.position = *position; | |
| 120 params.color = color; | |
| 121 if (clip) { | |
| 122 params.clip = *clip; | |
| 123 params.clip_is_null = false; | |
| 124 } else { | |
| 125 params.clip = PP_MakeRectFromXYWH(0, 0, 0, 0); | |
| 126 params.clip_is_null = true; | |
| 127 } | |
| 128 params.image_data_is_opaque = image_data_is_opaque; | |
| 129 | |
| 130 Dispatcher* dispatcher = PluginDispatcher::GetForInstance( | |
| 131 image_object->instance()); | |
| 132 PP_Bool result = PP_FALSE; | |
| 133 if (dispatcher) { | |
| 134 dispatcher->Send(new PpapiHostMsg_PPBFont_DrawTextAt( | |
| 135 INTERFACE_ID_PPB_FONT, | |
| 136 SerializedVarSendInput(dispatcher, text->text), | |
| 137 params, &result)); | |
| 138 } | |
| 139 return result; | |
| 140 } | |
| 141 | |
| 142 int32_t MeasureText(PP_Resource font_id, const PP_TextRun_Dev* text) { | |
| 143 Font* object = PluginResource::GetAs<Font>(font_id); | |
| 144 if (!object) | |
| 145 return -1; | |
| 146 | |
| 147 Dispatcher* dispatcher = PluginDispatcher::GetForInstance(object->instance()); | |
| 148 int32_t result = 0; | |
| 149 if (dispatcher) { | |
| 150 dispatcher->Send(new PpapiHostMsg_PPBFont_MeasureText( | |
| 151 INTERFACE_ID_PPB_FONT, object->host_resource(), | |
| 152 SerializedVarSendInput(dispatcher, text->text), | |
| 153 text->rtl, text->override_direction, &result)); | |
| 154 } | |
| 155 return result; | |
| 156 } | |
| 157 | |
| 158 uint32_t CharacterOffsetForPixel(PP_Resource font_id, | |
| 159 const PP_TextRun_Dev* text, | |
| 160 int32_t pixel_position) { | |
| 161 Font* object = PluginResource::GetAs<Font>(font_id); | |
| 162 if (!object) | |
| 163 return -1; | |
| 164 | |
| 165 Dispatcher* dispatcher = PluginDispatcher::GetForInstance(object->instance()); | |
| 166 uint32_t result = 0; | |
| 167 if (dispatcher) { | |
| 168 dispatcher->Send(new PpapiHostMsg_PPBFont_CharacterOffsetForPixel( | |
| 169 INTERFACE_ID_PPB_FONT, object->host_resource(), | |
| 170 SerializedVarSendInput(dispatcher, text->text), | |
| 171 text->rtl, text->override_direction, pixel_position, &result)); | |
| 172 } | |
| 173 return result; | |
| 174 } | |
| 175 | |
| 176 int32_t PixelOffsetForCharacter(PP_Resource font_id, | |
| 177 const PP_TextRun_Dev* text, | |
| 178 uint32_t char_offset) { | |
| 179 Font* object = PluginResource::GetAs<Font>(font_id); | |
| 180 if (!object) | |
| 181 return -1; | |
| 182 | |
| 183 Dispatcher* dispatcher = PluginDispatcher::GetForInstance(object->instance()); | |
| 184 int32_t result = 0; | |
| 185 if (dispatcher) { | |
| 186 dispatcher->Send(new PpapiHostMsg_PPBFont_PixelOffsetForCharacter( | |
| 187 INTERFACE_ID_PPB_FONT, object->host_resource(), | |
| 188 SerializedVarSendInput(dispatcher, text->text), | |
| 189 text->rtl, text->override_direction, char_offset, &result)); | |
| 190 } | |
| 191 return result; | |
| 192 } | |
| 193 | |
| 194 const PPB_Font_Dev font_interface = { | |
| 195 &Create, | |
| 196 &IsFont, | |
| 197 &Describe, | |
| 198 &DrawTextAt, | |
| 199 &MeasureText, | |
| 200 &CharacterOffsetForPixel, | |
| 201 &PixelOffsetForCharacter | |
| 202 }; | |
| 203 | |
| 204 InterfaceProxy* CreateFontProxy(Dispatcher* dispatcher, | 37 InterfaceProxy* CreateFontProxy(Dispatcher* dispatcher, |
| 205 const void* target_interface) { | 38 const void* target_interface) { |
| 206 return new PPB_Font_Proxy(dispatcher, target_interface); | 39 return new PPB_Font_Proxy(dispatcher, target_interface); |
| 207 } | 40 } |
| 208 | 41 |
| 209 } // namespace | 42 } // namespace |
| 210 | 43 |
| 211 PPB_Font_Proxy::PPB_Font_Proxy(Dispatcher* dispatcher, | 44 PPB_Font_Proxy::PPB_Font_Proxy(Dispatcher* dispatcher, |
| 212 const void* target_interface) | 45 const void* target_interface) |
| 213 : InterfaceProxy(dispatcher, target_interface) { | 46 : InterfaceProxy(dispatcher, target_interface) { |
| 214 } | 47 } |
| 215 | 48 |
| 216 PPB_Font_Proxy::~PPB_Font_Proxy() { | 49 PPB_Font_Proxy::~PPB_Font_Proxy() { |
| 217 } | 50 } |
| 218 | 51 |
| 219 // static | 52 // static |
| 220 const InterfaceProxy::Info* PPB_Font_Proxy::GetInfo() { | 53 const InterfaceProxy::Info* PPB_Font_Proxy::GetInfo() { |
| 221 static const Info info = { | 54 static const Info info = { |
| 222 &font_interface, | 55 ::ppapi::thunk::GetPPB_Font_Thunk(), |
| 223 PPB_FONT_DEV_INTERFACE, | 56 PPB_FONT_DEV_INTERFACE, |
| 224 INTERFACE_ID_PPB_FONT, | 57 INTERFACE_ID_PPB_FONT, |
| 225 false, | 58 false, |
| 226 &CreateFontProxy, | 59 &CreateFontProxy, |
| 227 }; | 60 }; |
| 228 return &info; | 61 return &info; |
| 229 } | 62 } |
| 230 | 63 |
| 231 bool PPB_Font_Proxy::OnMessageReceived(const IPC::Message& msg) { | 64 bool PPB_Font_Proxy::OnMessageReceived(const IPC::Message& msg) { |
| 232 bool handled = true; | 65 // There aren't any font messages. |
| 233 IPC_BEGIN_MESSAGE_MAP(PPB_Font_Proxy, msg) | 66 NOTREACHED(); |
| 234 IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBFont_Create, | 67 return false; |
| 235 OnMsgCreate) | |
| 236 IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBFont_DrawTextAt, | |
| 237 OnMsgDrawTextAt) | |
| 238 IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBFont_MeasureText, | |
| 239 OnMsgMeasureText) | |
| 240 IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBFont_CharacterOffsetForPixel, | |
| 241 OnMsgCharacterOffsetForPixel) | |
| 242 IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBFont_PixelOffsetForCharacter, | |
| 243 OnMsgPixelOffsetForCharacter) | |
| 244 IPC_MESSAGE_UNHANDLED(handled = false) | |
| 245 IPC_END_MESSAGE_MAP() | |
| 246 return handled; | |
| 247 } | 68 } |
| 248 | 69 |
| 249 void PPB_Font_Proxy::OnMsgCreate( | 70 Font::Font(const HostResource& resource, |
| 250 PP_Instance instance, | 71 const PP_FontDescription_Dev& desc) |
| 251 const SerializedFontDescription& in_description, | 72 : PluginResource(resource), |
| 252 HostResource* result, | 73 webkit_event_(false, false) { |
| 253 SerializedFontDescription* out_description, | 74 const std::string* face = PluginVarTracker::GetInstance()->GetExistingString( |
| 254 std::string* out_metrics) { | 75 desc.face); |
| 255 // Convert the face name in the input description. | |
| 256 PP_FontDescription_Dev in_pp_desc; | |
| 257 in_description.SetToPPFontDescription(dispatcher(), &in_pp_desc, false); | |
| 258 | 76 |
| 259 // Make sure the output is always defined so we can still serialize it back | 77 WebKitForwarding* forwarding = GetDispatcher()->GetWebKitForwarding(); |
| 260 // to the plugin below. | |
| 261 PP_FontDescription_Dev out_pp_desc; | |
| 262 memset(&out_pp_desc, 0, sizeof(PP_FontDescription_Dev)); | |
| 263 out_pp_desc.face = PP_MakeUndefined(); | |
| 264 | 78 |
| 265 result->SetHostResource(instance, | 79 WebKitForwarding::Font* result = NULL; |
| 266 ppb_font_target()->Create(instance, &in_pp_desc)); | 80 RunOnWebKitThread(base::Bind(&WebKitForwarding::CreateFontForwarding, |
| 267 if (!result->is_null()) { | 81 base::Unretained(forwarding), |
| 268 // Get the metrics and resulting description to return to the browser. | 82 &webkit_event_, desc, |
| 269 PP_FontMetrics_Dev metrics; | 83 face ? *face : std::string(), &result)); |
| 270 if (ppb_font_target()->Describe(result->host_resource(), &out_pp_desc, | 84 font_forwarding_.reset(result); |
| 271 &metrics)) { | 85 } |
| 272 out_metrics->assign(reinterpret_cast<const char*>(&metrics), | 86 |
| 273 sizeof(PP_FontMetrics_Dev)); | 87 Font::~Font() { |
| 274 } | 88 } |
| 89 |
| 90 ppapi::thunk::PPB_Font_API* Font::AsFont_API() { |
| 91 return this; |
| 92 } |
| 93 |
| 94 Font* Font::AsFont() { |
| 95 return this; |
| 96 } |
| 97 |
| 98 PP_Bool Font::Describe(PP_FontDescription_Dev* description, |
| 99 PP_FontMetrics_Dev* metrics) { |
| 100 std::string face; |
| 101 PP_Bool result = PP_FALSE; |
| 102 RunOnWebKitThread(base::Bind(&WebKitForwarding::Font::Describe, |
| 103 base::Unretained(font_forwarding_.get()), |
| 104 &webkit_event_, description, &face, metrics, |
| 105 &result)); |
| 106 |
| 107 if (result == PP_TRUE) { |
| 108 description->face.type = PP_VARTYPE_STRING; |
| 109 description->face.value.as_id = |
| 110 PluginVarTracker::GetInstance()->MakeString(face); |
| 111 } else { |
| 112 description->face.type = PP_VARTYPE_UNDEFINED; |
| 113 } |
| 114 return result; |
| 115 } |
| 116 |
| 117 PP_Bool Font::DrawTextAt(PP_Resource pp_image_data, |
| 118 const PP_TextRun_Dev* text, |
| 119 const PP_Point* position, |
| 120 uint32_t color, |
| 121 const PP_Rect* clip, |
| 122 PP_Bool image_data_is_opaque) { |
| 123 // Convert to an ImageData object. |
| 124 ppapi::shared_impl::ResourceObjectBase* image_base = |
| 125 ppapi::shared_impl::TrackerBase::Get()->GetResourceAPI(pp_image_data); |
| 126 if (!image_base) |
| 127 return PP_FALSE; |
| 128 PPB_ImageData_API* image_api = image_base->GetAs<PPB_ImageData_API>(); |
| 129 if (!image_api) |
| 130 return PP_FALSE; |
| 131 ImageData* image_data = static_cast<ImageData*>(image_api); |
| 132 |
| 133 skia::PlatformCanvas* canvas = image_data->mapped_canvas(); |
| 134 bool needs_unmapping = false; |
| 135 if (!canvas) { |
| 136 needs_unmapping = true; |
| 137 image_data->Map(); |
| 138 canvas = image_data->mapped_canvas(); |
| 139 if (!canvas) |
| 140 return PP_FALSE; // Failure mapping. |
| 275 } | 141 } |
| 276 | 142 |
| 277 // This must always get called or it will assert when trying to serialize | 143 WebKitForwarding::Font::TextRun run; |
| 278 // the un-filled-in SerializedFontDescription as the return value. | 144 if (!PPTextRunToTextRun(text, &run)) { |
| 279 out_description->SetFromPPFontDescription(dispatcher(), out_pp_desc, false); | 145 if (needs_unmapping) |
| 146 image_data->Unmap(); |
| 147 return PP_FALSE; |
| 148 } |
| 149 RunOnWebKitThread(base::Bind( |
| 150 &WebKitForwarding::Font::DrawTextAt, |
| 151 base::Unretained(font_forwarding_.get()), |
| 152 &webkit_event_, |
| 153 WebKitForwarding::Font::DrawTextParams(canvas, run, position, color, |
| 154 clip, image_data_is_opaque))); |
| 155 |
| 156 if (needs_unmapping) |
| 157 image_data->Unmap(); |
| 158 return PP_TRUE; |
| 280 } | 159 } |
| 281 | 160 |
| 282 void PPB_Font_Proxy::OnMsgDrawTextAt(SerializedVarReceiveInput text, | 161 int32_t Font::MeasureText(const PP_TextRun_Dev* text) { |
| 283 const PPBFont_DrawTextAt_Params& params, | 162 WebKitForwarding::Font::TextRun run; |
| 284 PP_Bool* result) { | 163 if (!PPTextRunToTextRun(text, &run)) |
| 285 PP_TextRun_Dev run; | 164 return -1; |
| 286 run.text = text.Get(dispatcher()); | 165 int32_t result = -1; |
| 287 run.rtl = params.text_is_rtl; | 166 RunOnWebKitThread(base::Bind(&WebKitForwarding::Font::MeasureText, |
| 288 run.override_direction = params.override_direction; | 167 base::Unretained(font_forwarding_.get()), |
| 289 | 168 &webkit_event_, run, &result)); |
| 290 *result = ppb_font_target()->DrawTextAt(params.font.host_resource(), | 169 return result; |
| 291 params.image_data.host_resource(), &run, ¶ms.position, params.color, | |
| 292 params.clip_is_null ? NULL : ¶ms.clip, params.image_data_is_opaque); | |
| 293 } | 170 } |
| 294 | 171 |
| 295 void PPB_Font_Proxy::OnMsgMeasureText(HostResource font, | 172 uint32_t Font::CharacterOffsetForPixel(const PP_TextRun_Dev* text, |
| 296 SerializedVarReceiveInput text, | 173 int32_t pixel_position) { |
| 297 PP_Bool text_is_rtl, | 174 WebKitForwarding::Font::TextRun run; |
| 298 PP_Bool override_direction, | 175 if (!PPTextRunToTextRun(text, &run)) |
| 299 int32_t* result) { | 176 return -1; |
| 300 PP_TextRun_Dev run; | 177 uint32_t result = -1; |
| 301 run.text = text.Get(dispatcher()); | 178 RunOnWebKitThread(base::Bind(&WebKitForwarding::Font::CharacterOffsetForPixel, |
| 302 run.rtl = text_is_rtl; | 179 base::Unretained(font_forwarding_.get()), |
| 303 run.override_direction = override_direction; | 180 &webkit_event_, run, pixel_position, &result)); |
| 304 | 181 return result; |
| 305 *result = ppb_font_target()->MeasureText(font.host_resource(), &run); | |
| 306 } | 182 } |
| 307 | 183 |
| 308 void PPB_Font_Proxy::OnMsgCharacterOffsetForPixel( | 184 int32_t Font::PixelOffsetForCharacter(const PP_TextRun_Dev* text, |
| 309 HostResource font, | 185 uint32_t char_offset) { |
| 310 SerializedVarReceiveInput text, | 186 WebKitForwarding::Font::TextRun run; |
| 311 PP_Bool text_is_rtl, | 187 if (!PPTextRunToTextRun(text, &run)) |
| 312 PP_Bool override_direction, | 188 return -1; |
| 313 int32_t pixel_pos, | 189 int32_t result = -1; |
| 314 uint32_t* result) { | 190 RunOnWebKitThread(base::Bind(&WebKitForwarding::Font::PixelOffsetForCharacter, |
| 315 PP_TextRun_Dev run; | 191 base::Unretained(font_forwarding_.get()), |
| 316 run.text = text.Get(dispatcher()); | 192 &webkit_event_, run, char_offset, &result)); |
| 317 run.rtl = text_is_rtl; | 193 return result; |
| 318 run.override_direction = override_direction; | |
| 319 | |
| 320 *result = ppb_font_target()->CharacterOffsetForPixel(font.host_resource(), | |
| 321 &run, pixel_pos); | |
| 322 } | 194 } |
| 323 | 195 |
| 324 void PPB_Font_Proxy::OnMsgPixelOffsetForCharacter( | 196 void Font::RunOnWebKitThread(const base::Closure& task) { |
| 325 HostResource font, | 197 GetDispatcher()->PostToWebKitThread(FROM_HERE, task); |
| 326 SerializedVarReceiveInput text, | 198 webkit_event_.Wait(); |
| 327 PP_Bool text_is_rtl, | |
| 328 PP_Bool override_direction, | |
| 329 uint32_t char_offset, | |
| 330 int32_t* result) { | |
| 331 PP_TextRun_Dev run; | |
| 332 run.text = text.Get(dispatcher()); | |
| 333 run.rtl = text_is_rtl; | |
| 334 run.override_direction = override_direction; | |
| 335 | |
| 336 *result = ppb_font_target()->PixelOffsetForCharacter(font.host_resource(), | |
| 337 &run, char_offset); | |
| 338 } | 199 } |
| 339 | 200 |
| 340 } // namespace proxy | 201 } // namespace proxy |
| 341 } // namespace pp | 202 } // namespace pp |
| OLD | NEW |