/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "TestCommon.h" #include "nsIComponentRegistrar.h" #include "nsIStreamTransportService.h" #include "nsIAsyncInputStream.h" #include "nsIProgressEventSink.h" #include "nsIInterfaceRequestor.h" #include "nsIInterfaceRequestorUtils.h" #include "nsIRequest.h" #include "nsIServiceManager.h" #include "nsIComponentManager.h" #include "nsCOMPtr.h" #include "nsMemory.h" #include "nsStringAPI.h" #include "nsIFileStreams.h" #include "nsIStreamListener.h" #include "nsIFile.h" #include "nsNetUtil.h" #include "nsAutoLock.h" #include "mozilla/Logging.h" #include "prenv.h" //////////////////////////////////////////////////////////////////////////////// // // set NSPR_LOG_MODULES=Test:5 // static PRLogModuleInfo *gTestLog = nullptr; #define LOG(args) MOZ_LOG(gTestLog, mozilla::LogLevel::Debug, args) //////////////////////////////////////////////////////////////////////////////// static NS_DEFINE_CID(kStreamTransportServiceCID, NS_STREAMTRANSPORTSERVICE_CID); //////////////////////////////////////////////////////////////////////////////// #define CHUNK_SIZE 500 class MyCopier : public nsIInputStreamCallback , public nsIOutputStreamCallback { public: NS_DECL_THREADSAFE_ISUPPORTS MyCopier() : mLock(nullptr) , mInputCondition(NS_OK) { } virtual ~MyCopier() { if (mLock) nsAutoLock::DestroyLock(mLock); if (mInput) mInput->Close(); if (mOutput) mOutput->Close(); } // called on any thread NS_IMETHOD OnInputStreamReady(nsIAsyncInputStream *inStr) { LOG(("OnInputStreamReady\n")); nsAutoLock lock(mLock); NS_ASSERTION(inStr == mInput, "unexpected stream"); Process_Locked(); return NS_OK; } // called on any thread NS_IMETHOD OnOutputStreamReady(nsIAsyncOutputStream *outStr) { LOG(("OnOutputStreamReady\n")); nsAutoLock lock(mLock); NS_ASSERTION(outStr == mOutput, "unexpected stream"); Process_Locked(); return NS_OK; } void Close_Locked() { LOG(("Close_Locked\n")); mOutput->Close(); mOutput = 0; mInput->Close(); mInput = 0; // post done copying event QuitPumpingEvents(); } void Process_Locked() { while (1) { mInputCondition = NS_OK; // reset uint32_t n; nsresult rv = mOutput->WriteSegments(FillOutputBuffer, this, CHUNK_SIZE, &n); if (NS_FAILED(rv) || (n == 0)) { if (rv == NS_BASE_STREAM_WOULD_BLOCK) mOutput->AsyncWait(this, 0, 0, nullptr); else if (mInputCondition == NS_BASE_STREAM_WOULD_BLOCK) mInput->AsyncWait(this, 0, 0, nullptr); else Close_Locked(); break; } } } nsresult AsyncCopy(nsITransport *srcTrans, nsITransport *destTrans) { mLock = nsAutoLock::NewLock("MyCopier::mLock"); if (!mLock) return NS_ERROR_OUT_OF_MEMORY; nsresult rv; nsCOMPtr inStr; rv = srcTrans->OpenInputStream(0, 0, 0, getter_AddRefs(inStr)); if (NS_FAILED(rv)) return rv; nsCOMPtr outStr; rv = destTrans->OpenOutputStream(0, 0, 0, getter_AddRefs(outStr)); if (NS_FAILED(rv)) return rv; mInput = do_QueryInterface(inStr); mOutput = do_QueryInterface(outStr); return mInput->AsyncWait(this, 0, 0, nullptr); } static NS_METHOD FillOutputBuffer(nsIOutputStream *outStr, void *closure, char *buffer, uint32_t offset, uint32_t count, uint32_t *countRead) { MyCopier *self = (MyCopier *) closure; nsresult rv = self->mInput->Read(buffer, count, countRead); if (NS_FAILED(rv)) self->mInputCondition = rv; else if (*countRead == 0) self->mInputCondition = NS_BASE_STREAM_CLOSED; return self->mInputCondition; } protected: PRLock *mLock; nsCOMPtr mInput; nsCOMPtr mOutput; nsresult mInputCondition; }; NS_IMPL_ISUPPORTS(MyCopier, nsIInputStreamCallback, nsIOutputStreamCallback) //////////////////////////////////////////////////////////////////////////////// /** * asynchronously copy file. */ static nsresult RunTest(nsIFile *srcFile, nsIFile *destFile) { nsresult rv; LOG(("RunTest\n")); nsCOMPtr sts = do_GetService(kStreamTransportServiceCID, &rv); if (NS_FAILED(rv)) return rv; nsCOMPtr srcStr; rv = NS_NewLocalFileInputStream(getter_AddRefs(srcStr), srcFile); if (NS_FAILED(rv)) return rv; nsCOMPtr destStr; rv = NS_NewLocalFileOutputStream(getter_AddRefs(destStr), destFile); if (NS_FAILED(rv)) return rv; nsCOMPtr srcTransport; rv = sts->CreateInputTransport(srcStr, int64_t(-1), int64_t(-1), true, getter_AddRefs(srcTransport)); if (NS_FAILED(rv)) return rv; nsCOMPtr destTransport; rv = sts->CreateOutputTransport(destStr, int64_t(-1), int64_t(-1), true, getter_AddRefs(destTransport)); if (NS_FAILED(rv)) return rv; MyCopier *copier = new MyCopier(); if (copier == nullptr) return NS_ERROR_OUT_OF_MEMORY; NS_ADDREF(copier); rv = copier->AsyncCopy(srcTransport, destTransport); if (NS_FAILED(rv)) return rv; PumpEvents(); NS_RELEASE(copier); return NS_OK; } //////////////////////////////////////////////////////////////////////////////// static nsresult RunBlockingTest(nsIFile *srcFile, nsIFile *destFile) { nsresult rv; LOG(("RunBlockingTest\n")); nsCOMPtr sts = do_GetService(kStreamTransportServiceCID, &rv); if (NS_FAILED(rv)) return rv; nsCOMPtr srcIn; rv = NS_NewLocalFileInputStream(getter_AddRefs(srcIn), srcFile); if (NS_FAILED(rv)) return rv; nsCOMPtr fileOut; rv = NS_NewLocalFileOutputStream(getter_AddRefs(fileOut), destFile); if (NS_FAILED(rv)) return rv; nsCOMPtr destTransport; rv = sts->CreateOutputTransport(fileOut, int64_t(-1), int64_t(-1), true, getter_AddRefs(destTransport)); if (NS_FAILED(rv)) return rv; nsCOMPtr destOut; rv = destTransport->OpenOutputStream(nsITransport::OPEN_BLOCKING, 100, 10, getter_AddRefs(destOut)); if (NS_FAILED(rv)) return rv; char buf[120]; uint32_t n; for (;;) { rv = srcIn->Read(buf, sizeof(buf), &n); if (NS_FAILED(rv) || (n == 0)) return rv; rv = destOut->Write(buf, n, &n); if (NS_FAILED(rv)) return rv; } return NS_OK; } //////////////////////////////////////////////////////////////////////////////// int main(int argc, char* argv[]) { if (test_common_init(&argc, &argv) != 0) return -1; nsresult rv; if (argc < 2) { printf("usage: %s \n", argv[0]); return -1; } char* fileName = argv[1]; { nsCOMPtr servMan; NS_InitXPCOM2(getter_AddRefs(servMan), nullptr, nullptr); nsCOMPtr registrar = do_QueryInterface(servMan); NS_ASSERTION(registrar, "Null nsIComponentRegistrar"); if (registrar) registrar->AutoRegister(nullptr); gTestLog = PR_NewLogModule("Test"); nsCOMPtr srcFile; rv = NS_NewNativeLocalFile(nsDependentCString(fileName), false, getter_AddRefs(srcFile)); if (NS_FAILED(rv)) return rv; nsCOMPtr destFile; rv = srcFile->Clone(getter_AddRefs(destFile)); if (NS_FAILED(rv)) return rv; nsAutoCString leafName; rv = destFile->GetNativeLeafName(leafName); if (NS_FAILED(rv)) return rv; nsAutoCString newName(leafName); newName.AppendLiteral(".1"); rv = destFile->SetNativeLeafName(newName); if (NS_FAILED(rv)) return rv; rv = RunTest(srcFile, destFile); NS_ASSERTION(NS_SUCCEEDED(rv), "RunTest failed"); newName = leafName; newName.AppendLiteral(".2"); rv = destFile->SetNativeLeafName(newName); if (NS_FAILED(rv)) return rv; rv = RunBlockingTest(srcFile, destFile); NS_ASSERTION(NS_SUCCEEDED(rv), "RunBlockingTest failed"); // give background threads a chance to finish whatever work they may // be doing. PR_Sleep(PR_SecondsToInterval(1)); } // this scopes the nsCOMPtrs // no nsCOMPtrs are allowed to be alive when you call NS_ShutdownXPCOM rv = NS_ShutdownXPCOM(nullptr); NS_ASSERTION(NS_SUCCEEDED(rv), "NS_ShutdownXPCOM failed"); return NS_OK; }