JSAPI2_AsyncDownloader.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732
  1. #include "JSAPI2_AsyncDownloader.h"
  2. #include "JSAPI2_Security.h"
  3. #include "main.h"
  4. #include "../Agave/Language/api_language.h"
  5. #include "JSAPI.h"
  6. #include "../nu/AutoChar.h"
  7. #include "../nu/AutoLock.h"
  8. #include "api.h"
  9. #include "..\Components\wac_network\wac_network_http_receiver_api.h"
  10. #include "resource.h"
  11. #include "../Plugins/General/gen_ml/ml.h"
  12. #include <api/service/svcs/svc_imgload.h>
  13. #include "JSAPI2_CallbackManager.h"
  14. #define SCRIPT_E_REPORTED 0x80020101
  15. #define SIMULTANEOUS_ASYNCDOWNLOADS 2
  16. std::vector<DownloadToken> asyncDownloads;
  17. Nullsoft::Utility::LockGuard asyncDownloadsLock;
  18. bool IsImage(const wchar_t *filename)
  19. {
  20. FOURCC imgload = svc_imageLoader::getServiceType();
  21. int n = (int) WASABI_API_SVC->service_getNumServices(imgload);
  22. for (int i=0; i<n; i++)
  23. {
  24. waServiceFactory *sf = WASABI_API_SVC->service_enumService(imgload,i);
  25. if (sf)
  26. {
  27. svc_imageLoader * l = (svc_imageLoader*)sf->getInterface();
  28. if (l)
  29. {
  30. if (l->isMine(filename))
  31. {
  32. sf->releaseInterface(l);
  33. return true;
  34. }
  35. sf->releaseInterface(l);
  36. }
  37. }
  38. }
  39. return false;
  40. }
  41. bool IsPlaylist(const wchar_t *filename)
  42. {
  43. if (!AGAVE_API_PLAYLISTMANAGER || !AGAVE_API_PLAYLISTMANAGER->CanLoad(filename))
  44. return false;
  45. return true;
  46. }
  47. bool IsMedia( const wchar_t *filename )
  48. {
  49. int a = 0;
  50. if ( !in_setmod_noplay( filename, &a ) )
  51. {
  52. return false;
  53. }
  54. return true;
  55. }
  56. namespace JSAPI2
  57. {
  58. class AsyncDownloaderAPICallback : public ifc_downloadManagerCallback
  59. {
  60. public:
  61. AsyncDownloaderAPICallback( const wchar_t *url, const wchar_t *destination_filepath, const wchar_t *onlineServiceId, const wchar_t *onlineServiceName )
  62. {
  63. this->hFile = INVALID_HANDLE_VALUE;
  64. this->url = _wcsdup( url );
  65. this->destination_filepath = _wcsdup( destination_filepath );
  66. this->onlineServiceId = _wcsdup( onlineServiceId );
  67. if ( onlineServiceName )
  68. this->onlineServiceName = _wcsdup( onlineServiceName );
  69. else
  70. this->onlineServiceName = NULL;
  71. this->totalSize = 0;
  72. this->downloaded = 0;
  73. ref_count = 1;
  74. }
  75. void OnInit(DownloadToken token)
  76. {
  77. callbackManager.OnInit(this->url, this->onlineServiceId);
  78. }
  79. void OnConnect(DownloadToken token)
  80. {
  81. // ---- retrieve total size
  82. api_httpreceiver *http = WAC_API_DOWNLOADMANAGER->GetReceiver(token);
  83. if (http)
  84. {
  85. this->totalSize = http->content_length();
  86. }
  87. // ---- create file handle
  88. hFile = CreateFileW(destination_filepath, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
  89. if ( hFile == INVALID_HANDLE_VALUE )
  90. {
  91. WAC_API_DOWNLOADMANAGER->CancelDownload(token);
  92. }
  93. callbackManager.OnConnect(this->url, this->onlineServiceId);
  94. }
  95. void OnData(DownloadToken token, void *data, size_t datalen)
  96. {
  97. // ---- OnConnect copied here due to dlmgr OnData called first
  98. // ---- retrieve total size
  99. api_httpreceiver *http = WAC_API_DOWNLOADMANAGER->GetReceiver(token);
  100. if ( !this->totalSize && http )
  101. {
  102. this->totalSize = http->content_length();
  103. }
  104. if ( hFile == INVALID_HANDLE_VALUE )
  105. {
  106. // ---- create file handle
  107. hFile = CreateFileW(destination_filepath, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
  108. if ( hFile == INVALID_HANDLE_VALUE )
  109. {
  110. WAC_API_DOWNLOADMANAGER->CancelDownload(token);
  111. return;
  112. }
  113. }
  114. // ---- OnConnect to be removed once dlmgr is fixed
  115. // ---- OnData
  116. // ---- if file handle is invalid, then cancel download
  117. if ( hFile == INVALID_HANDLE_VALUE )
  118. {
  119. WAC_API_DOWNLOADMANAGER->CancelDownload(token);
  120. return;
  121. }
  122. this->downloaded = (size_t)WAC_API_DOWNLOADMANAGER->GetBytesDownloaded(token);
  123. if ( datalen > 0 )
  124. {
  125. // ---- hFile is valid handle, and write to disk
  126. DWORD numWritten = 0;
  127. WriteFile(hFile, data, (DWORD)datalen, &numWritten, FALSE);
  128. // ---- failed writing the number of datalen characters, cancel download
  129. if (numWritten != datalen)
  130. {
  131. WAC_API_DOWNLOADMANAGER->CancelDownload(token);
  132. return;
  133. }
  134. }
  135. // TODO: if killswitch is turned on, then cancel download
  136. //if ( downloadStatus.UpdateStatus(p_token, this->downloaded, this->totalSize) )
  137. //{
  138. // WAC_API_DOWNLOADMANAGER->CancelDownload(p_token);
  139. //}
  140. callbackManager.OnData(url, this->downloaded, this->totalSize, this->onlineServiceId);
  141. }
  142. void OnCancel( DownloadToken p_token )
  143. {
  144. if ( hFile != INVALID_HANDLE_VALUE )
  145. {
  146. CloseHandle( hFile );
  147. DeleteFileW( destination_filepath );
  148. }
  149. this->resumeNextPendingDownload( p_token );
  150. callbackManager.OnCancel( url, this->onlineServiceId );
  151. this->Release();
  152. }
  153. void OnError(DownloadToken p_token, int error)
  154. {
  155. if ( hFile != INVALID_HANDLE_VALUE )
  156. {
  157. CloseHandle(hFile);
  158. DeleteFileW(destination_filepath);
  159. }
  160. this->resumeNextPendingDownload( p_token );
  161. callbackManager.OnError(url, error, this->onlineServiceId);
  162. this->Release();
  163. }
  164. void OnFinish( DownloadToken p_token )
  165. {
  166. if ( hFile != INVALID_HANDLE_VALUE )
  167. {
  168. CloseHandle( hFile );
  169. if ( IsMedia( PathFindFileNameW( destination_filepath ) ) )
  170. {
  171. LMDB_FILE_ADD_INFOW fi = { const_cast<wchar_t *>( destination_filepath ), -1, -1 };
  172. sendMlIpc( ML_IPC_DB_ADDORUPDATEFILEW, (WPARAM)&fi );
  173. sendMlIpc( ML_IPC_DB_SYNCDB, 0 );
  174. }
  175. }
  176. this->resumeNextPendingDownload( p_token );
  177. callbackManager.OnFinish( url, destination_filepath, this->onlineServiceId );
  178. this->Release();
  179. }
  180. int GetSource( wchar_t *source, size_t source_cch )
  181. {
  182. if ( this->onlineServiceName )
  183. return wcscpy_s( source, source_cch, this->onlineServiceName );
  184. else
  185. return 1;
  186. }
  187. int GetTitle( wchar_t *title, size_t title_cch )
  188. {
  189. return wcscpy_s( title, title_cch, PathFindFileNameW( this->destination_filepath ) );
  190. }
  191. int GetLocation( wchar_t *location, size_t location_cch )
  192. {
  193. return wcscpy_s( location, location_cch, this->destination_filepath );
  194. }
  195. size_t AddRef()
  196. {
  197. return InterlockedIncrement( (LONG *)&ref_count );
  198. }
  199. size_t Release()
  200. {
  201. if ( ref_count == 0 )
  202. return ref_count;
  203. LONG r = InterlockedDecrement( (LONG *)&ref_count );
  204. if ( r == 0 )
  205. delete( this );
  206. return r;
  207. }
  208. private: // private destructor so no one accidentally calls delete directly on this reference counted object
  209. ~AsyncDownloaderAPICallback()
  210. {
  211. if ( url )
  212. free( url );
  213. if ( destination_filepath )
  214. free( destination_filepath );
  215. if ( onlineServiceId )
  216. free( onlineServiceId );
  217. if ( onlineServiceName )
  218. free( onlineServiceName );
  219. }
  220. inline void resumeNextPendingDownload( DownloadToken p_token )
  221. {
  222. {
  223. Nullsoft::Utility::AutoLock lock( asyncDownloadsLock );
  224. size_t l_index = 0;
  225. for ( DownloadToken &l_download_token : asyncDownloads )
  226. {
  227. if ( l_download_token == p_token )
  228. {
  229. asyncDownloads.erase( asyncDownloads.begin() + l_index );
  230. break;
  231. }
  232. ++l_index;
  233. }
  234. }
  235. for ( DownloadToken &l_download_token : asyncDownloads )
  236. {
  237. if ( WAC_API_DOWNLOADMANAGER->IsPending( l_download_token ) )
  238. {
  239. WAC_API_DOWNLOADMANAGER->ResumePendingDownload( l_download_token );
  240. break;
  241. }
  242. }
  243. }
  244. protected:
  245. RECVS_DISPATCH;
  246. private:
  247. HANDLE hFile;
  248. wchar_t *url;
  249. wchar_t *destination_filepath;
  250. wchar_t *onlineServiceId;
  251. wchar_t *onlineServiceName;
  252. size_t totalSize;
  253. size_t downloaded;
  254. LONG ref_count;
  255. };
  256. }
  257. #define CBCLASS JSAPI2::AsyncDownloaderAPICallback
  258. START_DISPATCH;
  259. VCB( IFC_DOWNLOADMANAGERCALLBACK_ONFINISH, OnFinish )
  260. VCB( IFC_DOWNLOADMANAGERCALLBACK_ONCANCEL, OnCancel )
  261. VCB( IFC_DOWNLOADMANAGERCALLBACK_ONERROR, OnError )
  262. VCB( IFC_DOWNLOADMANAGERCALLBACK_ONDATA, OnData )
  263. VCB( IFC_DOWNLOADMANAGERCALLBACK_ONCONNECT, OnConnect )
  264. VCB( IFC_DOWNLOADMANAGERCALLBACK_ONINIT, OnInit )
  265. CB( IFC_DOWNLOADMANAGERCALLBACK_GETSOURCE, GetSource )
  266. CB( IFC_DOWNLOADMANAGERCALLBACK_GETTITLE, GetTitle )
  267. CB( IFC_DOWNLOADMANAGERCALLBACK_GETLOCATION, GetLocation )
  268. CB( ADDREF, AddRef )
  269. CB( RELEASE, Release )
  270. END_DISPATCH;
  271. #undef CBCLASS
  272. JSAPI2::AsyncDownloaderAPI::AsyncDownloaderAPI(const wchar_t *_key, JSAPI::ifc_info *_info)
  273. {
  274. info = _info;
  275. key = _key;
  276. refCount = 1;
  277. }
  278. JSAPI2::AsyncDownloaderAPI::~AsyncDownloaderAPI()
  279. {
  280. // just in case someone forgot
  281. JSAPI2::callbackManager.Deregister(this);
  282. size_t index = events.size();
  283. while(index--)
  284. {
  285. IDispatch *pEvent = events[index];
  286. if (NULL != pEvent)
  287. pEvent->Release();
  288. }
  289. }
  290. #define DISP_TABLE \
  291. CHECK_ID(DownloadMedia)\
  292. CHECK_ID(RegisterForEvents)\
  293. CHECK_ID(UnregisterFromEvents)\
  294. #define CHECK_ID(str) JSAPI_DISP_ENUMIFY(str),
  295. enum {
  296. DISP_TABLE
  297. };
  298. #undef CHECK_ID
  299. #define CHECK_ID(str)\
  300. if (CSTR_EQUAL == CompareStringW(lcid, NORM_IGNORECASE, rgszNames[i], -1, L## #str, -1))\
  301. { rgdispid[i] = JSAPI_DISP_ENUMIFY(str); continue; }
  302. HRESULT JSAPI2::AsyncDownloaderAPI::GetIDsOfNames(REFIID riid, OLECHAR FAR* FAR* rgszNames, unsigned int cNames, LCID lcid, DISPID FAR* rgdispid)
  303. {
  304. bool unknowns = false;
  305. for (unsigned int i = 0;i != cNames;i++)
  306. {
  307. DISP_TABLE;
  308. rgdispid[i] = DISPID_UNKNOWN;
  309. unknowns = true;
  310. }
  311. if (unknowns)
  312. return DISP_E_UNKNOWNNAME;
  313. else
  314. return S_OK;
  315. }
  316. HRESULT JSAPI2::AsyncDownloaderAPI::GetTypeInfo(unsigned int itinfo, LCID lcid, ITypeInfo FAR* FAR* pptinfo)
  317. {
  318. return E_NOTIMPL;
  319. }
  320. HRESULT JSAPI2::AsyncDownloaderAPI::GetTypeInfoCount(unsigned int FAR * pctinfo)
  321. {
  322. return E_NOTIMPL;
  323. }
  324. int CALLBACK WINAPI BrowseCallbackProc_Download(HWND hwnd, UINT uMsg, LPARAM lParam, LPARAM lpData)
  325. {
  326. if (uMsg == BFFM_INITIALIZED)
  327. {
  328. SendMessageW(hwnd, BFFM_SETSELECTIONW, 1, (LPARAM)lpData);
  329. // this is not nice but it fixes the selection not working correctly on all OSes
  330. EnumChildWindows(hwnd, browseEnumProc, 0);
  331. }
  332. if (uMsg == WM_CREATE) SetWindowTextW(hwnd,getStringW(IDS_SELDOWNLOADDIR,NULL,0));
  333. return 0;
  334. }
  335. void GetPathToStore(wchar_t path_to_store[MAX_PATH]);
  336. bool GetOnlineDownloadPath(const wchar_t *key, const wchar_t *svcname, wchar_t path_to_store[MAX_PATH])
  337. {
  338. //retrieve online service specific download path
  339. GetPrivateProfileStringW(key,L"downloadpath",NULL,path_to_store,MAX_PATH,JSAPI2_INIFILE);
  340. //if found then return, otherwise allow user to specify
  341. if (path_to_store && path_to_store[0]) return true;
  342. //default music folder
  343. GetPathToStore(path_to_store);
  344. //popup dialog to allow user select and specify online service download path
  345. BROWSEINFOW bi={0};
  346. wchar_t name[MAX_PATH] = {0};
  347. wchar_t title[256] = {0};
  348. bi.hwndOwner = g_dialog_box_parent?g_dialog_box_parent:hMainWindow;
  349. bi.pszDisplayName = name;
  350. StringCchPrintfW(title,256,getStringW(IDS_ONLINESERVICE_SELDOWNLOADDIR, 0, 0),svcname);
  351. bi.lpszTitle = title;
  352. bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_NEWDIALOGSTYLE;
  353. bi.lpfn = BrowseCallbackProc_Download;
  354. bi.lParam = (LPARAM)path_to_store;
  355. ITEMIDLIST *idlist = SHBrowseForFolderW(&bi);
  356. if (idlist)
  357. {
  358. SHGetPathFromIDListW(idlist, path_to_store);
  359. Shell_Free(idlist);
  360. WritePrivateProfileStringW(key,L"downloadpath",path_to_store,JSAPI2_INIFILE);
  361. return true;
  362. }
  363. return false;
  364. }
  365. void CleanNameForPath(wchar_t *name)
  366. {
  367. while (name && *name)
  368. {
  369. switch(*name)
  370. {
  371. case L'?':
  372. case L'*':
  373. case L'|':
  374. *name = L'_';
  375. break;
  376. case '/':
  377. case L'\\':
  378. case L':':
  379. *name = L'-';
  380. break;
  381. case L'\"':
  382. *name = L'\'';
  383. break;
  384. case L'<':
  385. *name = L'(';
  386. break;
  387. case L'>': *name = L')';
  388. break;
  389. }
  390. name++;
  391. }
  392. }
  393. HRESULT JSAPI2::AsyncDownloaderAPI::DownloadMedia(WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, unsigned int FAR *puArgErr)
  394. {
  395. JSAPI_VERIFY_METHOD(wFlags);
  396. JSAPI_VERIFY_PARAMCOUNT_OPTIONAL(pdispparams, 1, 3);
  397. JSAPI_VERIFY_PARAMTYPE(pdispparams, 1, VT_BSTR, puArgErr); //url
  398. JSAPI_VERIFY_PARAMTYPE_OPTIONAL(pdispparams, 2, VT_BSTR, puArgErr); //destination file
  399. JSAPI_VERIFY_PARAMTYPE_OPTIONAL(pdispparams, 3, VT_BOOL, puArgErr); //add to media library or not
  400. JSAPI_INIT_RESULT(pvarResult, VT_BOOL);
  401. if (security.GetActionAuthorization(L"downloader", L"downloadmedia", key, info, JSAPI2::api_security::ACTION_PROMPT) == JSAPI2::api_security::ACTION_ALLOWED)
  402. {
  403. JSAPI_SET_RESULT(pvarResult, boolVal, VARIANT_TRUE);
  404. const wchar_t *url = JSAPI_PARAM(pdispparams, 1).bstrVal;
  405. wchar_t *destFileSpec=JSAPI_PARAM_OPTIONAL(pdispparams, 2, bstrVal, PathFindFileNameW(url));
  406. //filter reserved characters in file name
  407. CleanNameForPath(destFileSpec);
  408. // verify that passed-in URL is a valid media type
  409. if (!url || !destFileSpec || (!IsImage(destFileSpec) && !IsPlaylist(destFileSpec) && !IsMedia(destFileSpec)))
  410. {
  411. JSAPI_SET_RESULT(pvarResult, boolVal, VARIANT_FALSE);
  412. return S_OK;
  413. }
  414. wchar_t path_to_store[MAX_PATH] = {0};
  415. if (GetOnlineDownloadPath(this->key, this->info->GetName(), path_to_store))
  416. {
  417. CreateDirectoryW(path_to_store, NULL);
  418. wchar_t destfile[MAX_PATH] = {0};
  419. PathCombineW(destfile, path_to_store, destFileSpec);
  420. JSAPI2::AsyncDownloaderAPICallback *callback = new JSAPI2::AsyncDownloaderAPICallback(url, destfile, key, this->info->GetName());
  421. {
  422. Nullsoft::Utility::AutoLock lock(asyncDownloadsLock);
  423. if (asyncDownloads.size() < SIMULTANEOUS_ASYNCDOWNLOADS)
  424. {
  425. DownloadToken dt = WAC_API_DOWNLOADMANAGER->DownloadEx(AutoChar(url), callback, api_downloadManager::DOWNLOADEX_CALLBACK | api_downloadManager::DOWNLOADEX_UI);
  426. asyncDownloads.push_back(dt);
  427. }
  428. else
  429. {
  430. DownloadToken dt = WAC_API_DOWNLOADMANAGER->DownloadEx(AutoChar(url), callback, api_downloadManager::DOWNLOADEX_CALLBACK | api_downloadManager::DOWNLOADEX_PENDING | api_downloadManager::DOWNLOADEX_UI);
  431. asyncDownloads.push_back(dt);
  432. }
  433. }
  434. }
  435. else
  436. {
  437. JSAPI_SET_RESULT(pvarResult, boolVal, VARIANT_FALSE);
  438. }
  439. }
  440. else
  441. {
  442. JSAPI_SET_RESULT(pvarResult, boolVal, VARIANT_FALSE);
  443. }
  444. return S_OK;
  445. }
  446. HRESULT JSAPI2::AsyncDownloaderAPI::RegisterForEvents(WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, unsigned int FAR *puArgErr)
  447. {
  448. JSAPI_VERIFY_METHOD(wFlags);
  449. JSAPI_VERIFY_PARAMCOUNT(pdispparams, 1);
  450. JSAPI_VERIFY_PARAMTYPE(pdispparams, 1, VT_DISPATCH, puArgErr);
  451. JSAPI_INIT_RESULT(pvarResult, VT_BOOL);
  452. switch (security.GetActionAuthorization(L"downloader", L"events", key, info, JSAPI2::api_security::ACTION_PROMPT))
  453. {
  454. case JSAPI2::api_security::ACTION_DISALLOWED:
  455. JSAPI_SET_RESULT(pvarResult, boolVal, VARIANT_FALSE);
  456. break;
  457. case JSAPI2::api_security::ACTION_ALLOWED:
  458. {
  459. /** if this is the first time someone is registering an event
  460. ** add ourselves to the callback manager
  461. */
  462. if (events.empty())
  463. JSAPI2::callbackManager.Register(this);
  464. IDispatch *event = JSAPI_PARAM(pdispparams, 1).pdispVal;
  465. event->AddRef();
  466. // TODO: benski> not sure, but we might need to: event->AddRef();
  467. events.push_back(event);
  468. JSAPI_SET_RESULT(pvarResult, boolVal, VARIANT_TRUE);
  469. }
  470. break;
  471. }
  472. return S_OK;
  473. }
  474. HRESULT JSAPI2::AsyncDownloaderAPI::UnregisterFromEvents(WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, unsigned int FAR *puArgErr)
  475. {
  476. JSAPI_VERIFY_METHOD(wFlags);
  477. JSAPI_VERIFY_PARAMCOUNT(pdispparams, 1);
  478. JSAPI_VERIFY_PARAMTYPE(pdispparams, 1, VT_DISPATCH, puArgErr);
  479. IDispatch *event = JSAPI_PARAM(pdispparams, 1).pdispVal;
  480. // TODO: benski> not sure, but we might need to: event->Release();
  481. size_t index = events.size();
  482. while(index--)
  483. {
  484. if (events[index] == event)
  485. {
  486. events.erase(events.begin() + index);
  487. event->Release();
  488. }
  489. }
  490. /** if we don't have any more event listeners
  491. ** remove ourselves from the callback manager
  492. */
  493. if (events.empty())
  494. JSAPI2::callbackManager.Deregister(this);
  495. return S_OK;
  496. }
  497. #undef CHECK_ID
  498. #define CHECK_ID(str) case JSAPI_DISP_ENUMIFY(str): return str(wFlags, pdispparams, pvarResult, puArgErr);
  499. HRESULT JSAPI2::AsyncDownloaderAPI::Invoke(DISPID dispid, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, EXCEPINFO FAR * pexecinfo, unsigned int FAR *puArgErr)
  500. {
  501. switch (dispid)
  502. {
  503. DISP_TABLE
  504. }
  505. return DISP_E_MEMBERNOTFOUND;
  506. }
  507. STDMETHODIMP JSAPI2::AsyncDownloaderAPI::QueryInterface(REFIID riid, PVOID *ppvObject)
  508. {
  509. if (!ppvObject)
  510. return E_POINTER;
  511. else if (IsEqualIID(riid, IID_IDispatch))
  512. *ppvObject = (IDispatch *)this;
  513. else if (IsEqualIID(riid, IID_IUnknown))
  514. *ppvObject = this;
  515. else
  516. {
  517. *ppvObject = NULL;
  518. return E_NOINTERFACE;
  519. }
  520. AddRef();
  521. return S_OK;
  522. }
  523. ULONG JSAPI2::AsyncDownloaderAPI::AddRef(void)
  524. {
  525. return InterlockedIncrement(&refCount);
  526. }
  527. ULONG JSAPI2::AsyncDownloaderAPI::Release(void)
  528. {
  529. LONG lRef = InterlockedDecrement(&refCount);
  530. if (lRef == 0) delete this;
  531. return lRef;
  532. }
  533. void JSAPI2::AsyncDownloaderAPI::InvokeEvent(const wchar_t *eventName, JSAPI::CallbackParameters::PropertyTemplate *parameters, size_t parametersCount)
  534. {
  535. size_t index = events.size();
  536. if (0 == index)
  537. {
  538. JSAPI2::callbackManager.Deregister(this);
  539. return;
  540. }
  541. JSAPI::CallbackParameters *eventData= new JSAPI::CallbackParameters;
  542. if (NULL == eventData) return;
  543. eventData->AddString(L"event", eventName);
  544. if (NULL != parameters && 0 != parametersCount)
  545. eventData->AddPropertyIndirect(parameters, parametersCount);
  546. HRESULT hr;
  547. while (index--)
  548. {
  549. IDispatch *pEvent = events[index];
  550. if (NULL != pEvent)
  551. {
  552. hr = JSAPI::InvokeEvent(eventData, pEvent);
  553. if (FAILED(hr) && SCRIPT_E_REPORTED != hr)
  554. {
  555. events.erase(events.begin() + index);
  556. pEvent->Release();
  557. }
  558. }
  559. }
  560. if (events.empty())
  561. JSAPI2::callbackManager.Deregister(this);
  562. eventData->Release();
  563. }
  564. void JSAPI2::AsyncDownloaderAPI::OnInit(const wchar_t *url)
  565. {
  566. JSAPI::CallbackParameters::PropertyTemplate parameter =
  567. {JSAPI::CallbackParameters::typeString, L"url", (ULONG_PTR)url};
  568. InvokeEvent(L"OnInit", &parameter, 1);
  569. }
  570. void JSAPI2::AsyncDownloaderAPI::OnConnect(const wchar_t *url)
  571. {
  572. JSAPI::CallbackParameters::PropertyTemplate parameter =
  573. {JSAPI::CallbackParameters::typeString, L"url", (ULONG_PTR)url};
  574. InvokeEvent(L"OnConnect", &parameter, 1);
  575. }
  576. void JSAPI2::AsyncDownloaderAPI::OnData(const wchar_t *url, size_t downloadedlen, size_t totallen)
  577. {
  578. JSAPI::CallbackParameters::PropertyTemplate parameter[3] =
  579. {{JSAPI::CallbackParameters::typeString, L"url", (ULONG_PTR)url},
  580. {JSAPI::CallbackParameters::typeLong, L"downloadedlen", (ULONG_PTR)downloadedlen},
  581. {JSAPI::CallbackParameters::typeLong, L"totallen", (ULONG_PTR)totallen}};
  582. InvokeEvent(L"OnData", &parameter[0], 3);
  583. }
  584. void JSAPI2::AsyncDownloaderAPI::OnCancel(const wchar_t *url)
  585. {
  586. JSAPI::CallbackParameters::PropertyTemplate parameter =
  587. {JSAPI::CallbackParameters::typeString, L"url", (ULONG_PTR)url};
  588. InvokeEvent(L"OnCancel", &parameter, 1);
  589. }
  590. void JSAPI2::AsyncDownloaderAPI::OnError(const wchar_t *url, int error)
  591. {
  592. JSAPI::CallbackParameters::PropertyTemplate parameter[2] =
  593. {{JSAPI::CallbackParameters::typeString, L"url", (ULONG_PTR)url},
  594. {JSAPI::CallbackParameters::typeLong, L"error", (ULONG_PTR)error}};
  595. InvokeEvent(L"OnError", &parameter[0], 2);
  596. }
  597. void JSAPI2::AsyncDownloaderAPI::OnFinish(const wchar_t *url, const wchar_t *destfilename)
  598. {
  599. JSAPI::CallbackParameters::PropertyTemplate parameter[2] =
  600. {{JSAPI::CallbackParameters::typeString, L"url", (ULONG_PTR)url},
  601. {JSAPI::CallbackParameters::typeString, L"destfilename", (ULONG_PTR)destfilename}};
  602. InvokeEvent(L"OnFinish", &parameter[0], 2);
  603. }
  604. const wchar_t *JSAPI2::AsyncDownloaderAPI::GetKey()
  605. {
  606. return this->key;
  607. }