Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(608)

Side by Side Diff: fpdfsdk/javascript/app.cpp

Issue 2391313002: Rename CPDFSDK_Environment to CPDFSDK_FormfillEnvironment (Closed)
Patch Set: Rebase to master Created 4 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2014 PDFium Authors. All rights reserved. 1 // Copyright 2014 PDFium 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 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com 5 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
6 6
7 #include "fpdfsdk/javascript/app.h" 7 #include "fpdfsdk/javascript/app.h"
8 8
9 #include <memory> 9 #include <memory>
10 #include <vector> 10 #include <vector>
11 11
12 #include "fpdfsdk/cpdfsdk_document.h" 12 #include "fpdfsdk/cpdfsdk_document.h"
13 #include "fpdfsdk/cpdfsdk_environment.h" 13 #include "fpdfsdk/cpdfsdk_formfillenvironment.h"
14 #include "fpdfsdk/cpdfsdk_interform.h" 14 #include "fpdfsdk/cpdfsdk_interform.h"
15 #include "fpdfsdk/javascript/Document.h" 15 #include "fpdfsdk/javascript/Document.h"
16 #include "fpdfsdk/javascript/JS_Define.h" 16 #include "fpdfsdk/javascript/JS_Define.h"
17 #include "fpdfsdk/javascript/JS_EventHandler.h" 17 #include "fpdfsdk/javascript/JS_EventHandler.h"
18 #include "fpdfsdk/javascript/JS_Object.h" 18 #include "fpdfsdk/javascript/JS_Object.h"
19 #include "fpdfsdk/javascript/JS_Value.h" 19 #include "fpdfsdk/javascript/JS_Value.h"
20 #include "fpdfsdk/javascript/cjs_context.h" 20 #include "fpdfsdk/javascript/cjs_context.h"
21 #include "fpdfsdk/javascript/cjs_runtime.h" 21 #include "fpdfsdk/javascript/cjs_runtime.h"
22 #include "fpdfsdk/javascript/resource.h" 22 #include "fpdfsdk/javascript/resource.h"
23 #include "third_party/base/stl_util.h" 23 #include "third_party/base/stl_util.h"
24 24
25 class GlobalTimer { 25 class GlobalTimer {
26 public: 26 public:
27 GlobalTimer(app* pObj, 27 GlobalTimer(app* pObj,
28 CPDFSDK_Environment* pEnv, 28 CPDFSDK_FormFillEnvironment* pEnv,
29 CJS_Runtime* pRuntime, 29 CJS_Runtime* pRuntime,
30 int nType, 30 int nType,
31 const CFX_WideString& script, 31 const CFX_WideString& script,
32 uint32_t dwElapse, 32 uint32_t dwElapse,
33 uint32_t dwTimeOut); 33 uint32_t dwTimeOut);
34 ~GlobalTimer(); 34 ~GlobalTimer();
35 35
36 static void Trigger(int nTimerID); 36 static void Trigger(int nTimerID);
37 static void Cancel(int nTimerID); 37 static void Cancel(int nTimerID);
38 38
39 bool IsOneShot() const { return m_nType == 1; } 39 bool IsOneShot() const { return m_nType == 1; }
40 uint32_t GetTimeOut() const { return m_dwTimeOut; } 40 uint32_t GetTimeOut() const { return m_dwTimeOut; }
41 int GetTimerID() const { return m_nTimerID; } 41 int GetTimerID() const { return m_nTimerID; }
42 CJS_Runtime* GetRuntime() const { return m_pRuntime.Get(); } 42 CJS_Runtime* GetRuntime() const { return m_pRuntime.Get(); }
43 CFX_WideString GetJScript() const { return m_swJScript; } 43 CFX_WideString GetJScript() const { return m_swJScript; }
44 44
45 private: 45 private:
46 using TimerMap = std::map<uint32_t, GlobalTimer*>; 46 using TimerMap = std::map<uint32_t, GlobalTimer*>;
47 static TimerMap* GetGlobalTimerMap(); 47 static TimerMap* GetGlobalTimerMap();
48 48
49 uint32_t m_nTimerID; 49 uint32_t m_nTimerID;
50 app* const m_pEmbedObj; 50 app* const m_pEmbedObj;
51 bool m_bProcessing; 51 bool m_bProcessing;
52 52
53 // data 53 // data
54 const int m_nType; // 0:Interval; 1:TimeOut 54 const int m_nType; // 0:Interval; 1:TimeOut
55 const uint32_t m_dwTimeOut; 55 const uint32_t m_dwTimeOut;
56 const CFX_WideString m_swJScript; 56 const CFX_WideString m_swJScript;
57 CJS_Runtime::ObservedPtr m_pRuntime; 57 CJS_Runtime::ObservedPtr m_pRuntime;
58 CPDFSDK_Environment* const m_pEnv; 58 CPDFSDK_FormFillEnvironment* const m_pEnv;
59 }; 59 };
60 60
61 GlobalTimer::GlobalTimer(app* pObj, 61 GlobalTimer::GlobalTimer(app* pObj,
62 CPDFSDK_Environment* pEnv, 62 CPDFSDK_FormFillEnvironment* pEnv,
63 CJS_Runtime* pRuntime, 63 CJS_Runtime* pRuntime,
64 int nType, 64 int nType,
65 const CFX_WideString& script, 65 const CFX_WideString& script,
66 uint32_t dwElapse, 66 uint32_t dwElapse,
67 uint32_t dwTimeOut) 67 uint32_t dwTimeOut)
68 : m_nTimerID(0), 68 : m_nTimerID(0),
69 m_pEmbedObj(pObj), 69 m_pEmbedObj(pObj),
70 m_bProcessing(false), 70 m_bProcessing(false),
71 m_nType(nType), 71 m_nType(nType),
72 m_dwTimeOut(dwTimeOut), 72 m_dwTimeOut(dwTimeOut),
(...skipping 137 matching lines...) Expand 10 before | Expand all | Expand 10 after
210 app::~app() { 210 app::~app() {
211 } 211 }
212 212
213 FX_BOOL app::activeDocs(IJS_Context* cc, 213 FX_BOOL app::activeDocs(IJS_Context* cc,
214 CJS_PropValue& vp, 214 CJS_PropValue& vp,
215 CFX_WideString& sError) { 215 CFX_WideString& sError) {
216 if (!vp.IsGetting()) 216 if (!vp.IsGetting())
217 return FALSE; 217 return FALSE;
218 218
219 CJS_Context* pContext = (CJS_Context*)cc; 219 CJS_Context* pContext = (CJS_Context*)cc;
220 CPDFSDK_Environment* pEnv = pContext->GetReaderEnv(); 220 CPDFSDK_FormFillEnvironment* pEnv = pContext->GetReaderEnv();
221 CJS_Runtime* pRuntime = pContext->GetJSRuntime(); 221 CJS_Runtime* pRuntime = pContext->GetJSRuntime();
222 CPDFSDK_Document* pCurDoc = pContext->GetReaderDocument(); 222 CPDFSDK_Document* pCurDoc = pContext->GetReaderDocument();
223 CJS_Array aDocs; 223 CJS_Array aDocs;
224 if (CPDFSDK_Document* pDoc = pEnv->GetSDKDocument()) { 224 if (CPDFSDK_Document* pDoc = pEnv->GetSDKDocument()) {
225 CJS_Document* pJSDocument = nullptr; 225 CJS_Document* pJSDocument = nullptr;
226 if (pDoc == pCurDoc) { 226 if (pDoc == pCurDoc) {
227 v8::Local<v8::Object> pObj = pRuntime->GetThisObj(); 227 v8::Local<v8::Object> pObj = pRuntime->GetThisObj();
228 if (CFXJS_Engine::GetObjDefnID(pObj) == CJS_Document::g_nObjDefnID) { 228 if (CFXJS_Engine::GetObjDefnID(pObj) == CJS_Document::g_nObjDefnID) {
229 pJSDocument = 229 pJSDocument =
230 static_cast<CJS_Document*>(pRuntime->GetObjectPrivate(pObj)); 230 static_cast<CJS_Document*>(pRuntime->GetObjectPrivate(pObj));
(...skipping 17 matching lines...) Expand all
248 248
249 FX_BOOL app::calculate(IJS_Context* cc, 249 FX_BOOL app::calculate(IJS_Context* cc,
250 CJS_PropValue& vp, 250 CJS_PropValue& vp,
251 CFX_WideString& sError) { 251 CFX_WideString& sError) {
252 if (vp.IsSetting()) { 252 if (vp.IsSetting()) {
253 bool bVP; 253 bool bVP;
254 vp >> bVP; 254 vp >> bVP;
255 m_bCalculate = (FX_BOOL)bVP; 255 m_bCalculate = (FX_BOOL)bVP;
256 256
257 CJS_Context* pContext = (CJS_Context*)cc; 257 CJS_Context* pContext = (CJS_Context*)cc;
258 CPDFSDK_Environment* pEnv = pContext->GetReaderEnv(); 258 CPDFSDK_FormFillEnvironment* pEnv = pContext->GetReaderEnv();
259 if (CPDFSDK_Document* pDoc = pEnv->GetSDKDocument()) 259 if (CPDFSDK_Document* pDoc = pEnv->GetSDKDocument())
260 pDoc->GetInterForm()->EnableCalculate((FX_BOOL)m_bCalculate); 260 pDoc->GetInterForm()->EnableCalculate((FX_BOOL)m_bCalculate);
261 } else { 261 } else {
262 vp << (bool)m_bCalculate; 262 vp << (bool)m_bCalculate;
263 } 263 }
264 return TRUE; 264 return TRUE;
265 } 265 }
266 266
267 FX_BOOL app::formsVersion(IJS_Context* cc, 267 FX_BOOL app::formsVersion(IJS_Context* cc,
268 CJS_PropValue& vp, 268 CJS_PropValue& vp,
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after
314 vp << JS_NUM_VIEWERVERSION; 314 vp << JS_NUM_VIEWERVERSION;
315 return TRUE; 315 return TRUE;
316 } 316 }
317 317
318 FX_BOOL app::platform(IJS_Context* cc, 318 FX_BOOL app::platform(IJS_Context* cc,
319 CJS_PropValue& vp, 319 CJS_PropValue& vp,
320 CFX_WideString& sError) { 320 CFX_WideString& sError) {
321 if (!vp.IsGetting()) 321 if (!vp.IsGetting())
322 return FALSE; 322 return FALSE;
323 #ifdef PDF_ENABLE_XFA 323 #ifdef PDF_ENABLE_XFA
324 CPDFSDK_Environment* pEnv = 324 CPDFSDK_FormFillEnvironment* pEnv =
325 static_cast<CJS_Context*>(cc)->GetJSRuntime()->GetReaderEnv(); 325 static_cast<CJS_Context*>(cc)->GetJSRuntime()->GetReaderEnv();
326 if (!pEnv) 326 if (!pEnv)
327 return FALSE; 327 return FALSE;
328 CFX_WideString platfrom = pEnv->GetPlatform(); 328 CFX_WideString platfrom = pEnv->GetPlatform();
329 if (!platfrom.IsEmpty()) { 329 if (!platfrom.IsEmpty()) {
330 vp << platfrom; 330 vp << platfrom;
331 return TRUE; 331 return TRUE;
332 } 332 }
333 #endif 333 #endif
334 vp << JS_STR_PLATFORM; 334 vp << JS_STR_PLATFORM;
335 return TRUE; 335 return TRUE;
336 } 336 }
337 337
338 FX_BOOL app::language(IJS_Context* cc, 338 FX_BOOL app::language(IJS_Context* cc,
339 CJS_PropValue& vp, 339 CJS_PropValue& vp,
340 CFX_WideString& sError) { 340 CFX_WideString& sError) {
341 if (!vp.IsGetting()) 341 if (!vp.IsGetting())
342 return FALSE; 342 return FALSE;
343 #ifdef PDF_ENABLE_XFA 343 #ifdef PDF_ENABLE_XFA
344 CPDFSDK_Environment* pEnv = 344 CPDFSDK_FormFillEnvironment* pEnv =
345 static_cast<CJS_Context*>(cc)->GetJSRuntime()->GetReaderEnv(); 345 static_cast<CJS_Context*>(cc)->GetJSRuntime()->GetReaderEnv();
346 if (!pEnv) 346 if (!pEnv)
347 return FALSE; 347 return FALSE;
348 CFX_WideString language = pEnv->GetLanguage(); 348 CFX_WideString language = pEnv->GetLanguage();
349 if (!language.IsEmpty()) { 349 if (!language.IsEmpty()) {
350 vp << language; 350 vp << language;
351 return TRUE; 351 return TRUE;
352 } 352 }
353 #endif 353 #endif
354 vp << JS_STR_LANGUANGE; 354 vp << JS_STR_LANGUANGE;
355 return TRUE; 355 return TRUE;
356 } 356 }
357 357
358 // creates a new fdf object that contains no data 358 // creates a new fdf object that contains no data
359 // comment: need reader support 359 // comment: need reader support
360 // note: 360 // note:
361 // CFDF_Document * CPDFSDK_Environment::NewFDF(); 361 // CFDF_Document * CPDFSDK_FormFillEnvironment::NewFDF();
362 FX_BOOL app::newFDF(IJS_Context* cc, 362 FX_BOOL app::newFDF(IJS_Context* cc,
363 const std::vector<CJS_Value>& params, 363 const std::vector<CJS_Value>& params,
364 CJS_Value& vRet, 364 CJS_Value& vRet,
365 CFX_WideString& sError) { 365 CFX_WideString& sError) {
366 return TRUE; 366 return TRUE;
367 } 367 }
368 // opens a specified pdf document and returns its document object 368 // opens a specified pdf document and returns its document object
369 // comment:need reader support 369 // comment:need reader support
370 // note: as defined in js reference, the proto of this function's fourth 370 // note: as defined in js reference, the proto of this function's fourth
371 // parmeters, how old an fdf document while do not show it. 371 // parmeters, how old an fdf document while do not show it.
372 // CFDF_Document * CPDFSDK_Environment::OpenFDF(string strPath,bool bUserConv); 372 // CFDF_Document * CPDFSDK_FormFillEnvironment::OpenFDF(string strPath,bool
373 // bUserConv);
373 374
374 FX_BOOL app::openFDF(IJS_Context* cc, 375 FX_BOOL app::openFDF(IJS_Context* cc,
375 const std::vector<CJS_Value>& params, 376 const std::vector<CJS_Value>& params,
376 CJS_Value& vRet, 377 CJS_Value& vRet,
377 CFX_WideString& sError) { 378 CFX_WideString& sError) {
378 return TRUE; 379 return TRUE;
379 } 380 }
380 381
381 FX_BOOL app::alert(IJS_Context* cc, 382 FX_BOOL app::alert(IJS_Context* cc,
382 const std::vector<CJS_Value>& params, 383 const std::vector<CJS_Value>& params,
383 CJS_Value& vRet, 384 CJS_Value& vRet,
384 CFX_WideString& sError) { 385 CFX_WideString& sError) {
385 CJS_Runtime* pRuntime = CJS_Runtime::FromContext(cc); 386 CJS_Runtime* pRuntime = CJS_Runtime::FromContext(cc);
386 std::vector<CJS_Value> newParams = JS_ExpandKeywordParams( 387 std::vector<CJS_Value> newParams = JS_ExpandKeywordParams(
387 pRuntime, params, 4, L"cMsg", L"nIcon", L"nType", L"cTitle"); 388 pRuntime, params, 4, L"cMsg", L"nIcon", L"nType", L"cTitle");
388 389
389 if (newParams[0].GetType() == CJS_Value::VT_unknown) { 390 if (newParams[0].GetType() == CJS_Value::VT_unknown) {
390 sError = JSGetStringFromID(IDS_STRING_JSPARAMERROR); 391 sError = JSGetStringFromID(IDS_STRING_JSPARAMERROR);
391 return FALSE; 392 return FALSE;
392 } 393 }
393 394
394 CPDFSDK_Environment* pEnv = pRuntime->GetReaderEnv(); 395 CPDFSDK_FormFillEnvironment* pEnv = pRuntime->GetReaderEnv();
395 if (!pEnv) { 396 if (!pEnv) {
396 vRet = CJS_Value(pRuntime, 0); 397 vRet = CJS_Value(pRuntime, 0);
397 return TRUE; 398 return TRUE;
398 } 399 }
399 400
400 CFX_WideString swMsg; 401 CFX_WideString swMsg;
401 if (newParams[0].GetType() == CJS_Value::VT_object) { 402 if (newParams[0].GetType() == CJS_Value::VT_object) {
402 CJS_Array carray; 403 CJS_Array carray;
403 if (newParams[0].ConvertToArray(pRuntime, carray)) { 404 if (newParams[0].ConvertToArray(pRuntime, carray)) {
404 swMsg = L"["; 405 swMsg = L"[";
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
440 pRuntime->EndBlock(); 441 pRuntime->EndBlock();
441 return TRUE; 442 return TRUE;
442 } 443 }
443 444
444 FX_BOOL app::beep(IJS_Context* cc, 445 FX_BOOL app::beep(IJS_Context* cc,
445 const std::vector<CJS_Value>& params, 446 const std::vector<CJS_Value>& params,
446 CJS_Value& vRet, 447 CJS_Value& vRet,
447 CFX_WideString& sError) { 448 CFX_WideString& sError) {
448 if (params.size() == 1) { 449 if (params.size() == 1) {
449 CJS_Runtime* pRuntime = CJS_Runtime::FromContext(cc); 450 CJS_Runtime* pRuntime = CJS_Runtime::FromContext(cc);
450 CPDFSDK_Environment* pEnv = pRuntime->GetReaderEnv(); 451 CPDFSDK_FormFillEnvironment* pEnv = pRuntime->GetReaderEnv();
451 pEnv->JS_appBeep(params[0].ToInt(pRuntime)); 452 pEnv->JS_appBeep(params[0].ToInt(pRuntime));
452 return TRUE; 453 return TRUE;
453 } 454 }
454 455
455 sError = JSGetStringFromID(IDS_STRING_JSPARAMERROR); 456 sError = JSGetStringFromID(IDS_STRING_JSPARAMERROR);
456 return FALSE; 457 return FALSE;
457 } 458 }
458 459
459 FX_BOOL app::findComponent(IJS_Context* cc, 460 FX_BOOL app::findComponent(IJS_Context* cc,
460 const std::vector<CJS_Value>& params, 461 const std::vector<CJS_Value>& params,
(...skipping 24 matching lines...) Expand all
485 486
486 CJS_Runtime* pRuntime = CJS_Runtime::FromContext(cc); 487 CJS_Runtime* pRuntime = CJS_Runtime::FromContext(cc);
487 CFX_WideString script = 488 CFX_WideString script =
488 params.size() > 0 ? params[0].ToCFXWideString(pRuntime) : L""; 489 params.size() > 0 ? params[0].ToCFXWideString(pRuntime) : L"";
489 if (script.IsEmpty()) { 490 if (script.IsEmpty()) {
490 sError = JSGetStringFromID(IDS_STRING_JSAFNUMBER_KEYSTROKE); 491 sError = JSGetStringFromID(IDS_STRING_JSAFNUMBER_KEYSTROKE);
491 return TRUE; 492 return TRUE;
492 } 493 }
493 494
494 uint32_t dwInterval = params.size() > 1 ? params[1].ToInt(pRuntime) : 1000; 495 uint32_t dwInterval = params.size() > 1 ? params[1].ToInt(pRuntime) : 1000;
495 CPDFSDK_Environment* pEnv = pRuntime->GetReaderEnv(); 496 CPDFSDK_FormFillEnvironment* pEnv = pRuntime->GetReaderEnv();
496 497
497 GlobalTimer* timerRef = 498 GlobalTimer* timerRef =
498 new GlobalTimer(this, pEnv, pRuntime, 0, script, dwInterval, 0); 499 new GlobalTimer(this, pEnv, pRuntime, 0, script, dwInterval, 0);
499 m_Timers.insert(std::unique_ptr<GlobalTimer>(timerRef)); 500 m_Timers.insert(std::unique_ptr<GlobalTimer>(timerRef));
500 501
501 v8::Local<v8::Object> pRetObj = 502 v8::Local<v8::Object> pRetObj =
502 pRuntime->NewFxDynamicObj(CJS_TimerObj::g_nObjDefnID); 503 pRuntime->NewFxDynamicObj(CJS_TimerObj::g_nObjDefnID);
503 CJS_TimerObj* pJS_TimerObj = 504 CJS_TimerObj* pJS_TimerObj =
504 static_cast<CJS_TimerObj*>(pRuntime->GetObjectPrivate(pRetObj)); 505 static_cast<CJS_TimerObj*>(pRuntime->GetObjectPrivate(pRetObj));
505 TimerObj* pTimerObj = static_cast<TimerObj*>(pJS_TimerObj->GetEmbedObject()); 506 TimerObj* pTimerObj = static_cast<TimerObj*>(pJS_TimerObj->GetEmbedObject());
(...skipping 13 matching lines...) Expand all
519 } 520 }
520 521
521 CJS_Runtime* pRuntime = CJS_Runtime::FromContext(cc); 522 CJS_Runtime* pRuntime = CJS_Runtime::FromContext(cc);
522 CFX_WideString script = params[0].ToCFXWideString(pRuntime); 523 CFX_WideString script = params[0].ToCFXWideString(pRuntime);
523 if (script.IsEmpty()) { 524 if (script.IsEmpty()) {
524 sError = JSGetStringFromID(IDS_STRING_JSAFNUMBER_KEYSTROKE); 525 sError = JSGetStringFromID(IDS_STRING_JSAFNUMBER_KEYSTROKE);
525 return TRUE; 526 return TRUE;
526 } 527 }
527 528
528 uint32_t dwTimeOut = params.size() > 1 ? params[1].ToInt(pRuntime) : 1000; 529 uint32_t dwTimeOut = params.size() > 1 ? params[1].ToInt(pRuntime) : 1000;
529 CPDFSDK_Environment* pEnv = pRuntime->GetReaderEnv(); 530 CPDFSDK_FormFillEnvironment* pEnv = pRuntime->GetReaderEnv();
530 531
531 GlobalTimer* timerRef = 532 GlobalTimer* timerRef =
532 new GlobalTimer(this, pEnv, pRuntime, 1, script, dwTimeOut, dwTimeOut); 533 new GlobalTimer(this, pEnv, pRuntime, 1, script, dwTimeOut, dwTimeOut);
533 m_Timers.insert(std::unique_ptr<GlobalTimer>(timerRef)); 534 m_Timers.insert(std::unique_ptr<GlobalTimer>(timerRef));
534 535
535 v8::Local<v8::Object> pRetObj = 536 v8::Local<v8::Object> pRetObj =
536 pRuntime->NewFxDynamicObj(CJS_TimerObj::g_nObjDefnID); 537 pRuntime->NewFxDynamicObj(CJS_TimerObj::g_nObjDefnID);
537 538
538 CJS_TimerObj* pJS_TimerObj = 539 CJS_TimerObj* pJS_TimerObj =
539 static_cast<CJS_TimerObj*>(pRuntime->GetObjectPrivate(pRetObj)); 540 static_cast<CJS_TimerObj*>(pRuntime->GetObjectPrivate(pRetObj));
(...skipping 272 matching lines...) Expand 10 before | Expand all | Expand 10 after
812 FX_BOOL app::media(IJS_Context* cc, CJS_PropValue& vp, CFX_WideString& sError) { 813 FX_BOOL app::media(IJS_Context* cc, CJS_PropValue& vp, CFX_WideString& sError) {
813 return FALSE; 814 return FALSE;
814 } 815 }
815 816
816 FX_BOOL app::execDialog(IJS_Context* cc, 817 FX_BOOL app::execDialog(IJS_Context* cc,
817 const std::vector<CJS_Value>& params, 818 const std::vector<CJS_Value>& params,
818 CJS_Value& vRet, 819 CJS_Value& vRet,
819 CFX_WideString& sError) { 820 CFX_WideString& sError) {
820 return TRUE; 821 return TRUE;
821 } 822 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698