From 117a60442d54e022b097a25cd22df54d0ea3d1c9 Mon Sep 17 00:00:00 2001 From: Jim Chen Date: Wed, 17 May 2017 13:06:21 -0400 Subject: [PATCH 01/37] Bug 1360321 - 1. Update linux_syscall_support.h in Breakpad; r=ted The last update left out a couple lines that should have been taken out, which only affected AArch64 builds. MozReview-Commit-ID: 6PTz2y4U2iD --- .../src/third_party/lss/linux_syscall_support.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/toolkit/crashreporter/google-breakpad/src/third_party/lss/linux_syscall_support.h b/toolkit/crashreporter/google-breakpad/src/third_party/lss/linux_syscall_support.h index 162a27e7777b2..a5bcb9b04a542 100644 --- a/toolkit/crashreporter/google-breakpad/src/third_party/lss/linux_syscall_support.h +++ b/toolkit/crashreporter/google-breakpad/src/third_party/lss/linux_syscall_support.h @@ -4345,8 +4345,6 @@ struct kernel_statfs { loff_t, o) LSS_INLINE _syscall3(int, readahead, int, f, loff_t, o, unsigned, c) - LSS_INLINE _syscall6(void *, mmap, void *, addr, size_t, length, int, prot, - int, flags, int, fd, int64_t, offset) #else #define __NR__pread64 __NR_pread64 #define __NR__pwrite64 __NR_pwrite64 @@ -4403,8 +4401,6 @@ struct kernel_statfs { #if defined(__aarch64__) LSS_INLINE _syscall3(int, dup3, int, s, int, d, int, f) - LSS_INLINE _syscall6(void *, mmap, void *, addr, size_t, length, int, prot, - int, flags, int, fd, int64_t, offset) LSS_INLINE _syscall4(int, newfstatat, int, dirfd, const char *, pathname, struct kernel_stat *, buf, int, flags) LSS_INLINE _syscall2(int, pipe2, int *, pipefd, int, flags) From 0ea3a6811babc6e0d51b7a63bc51825ae4a0614a Mon Sep 17 00:00:00 2001 From: Jim Chen Date: Wed, 17 May 2017 13:06:21 -0400 Subject: [PATCH 02/37] Bug 1360321 - 2. Fix cast warning on AArch64 in plugin code; r=snorp Fix a warning on AArch64 for casting to pointer. MozReview-Commit-ID: 6zjYdN8ta9d --- dom/plugins/base/android/ANPSurface.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dom/plugins/base/android/ANPSurface.cpp b/dom/plugins/base/android/ANPSurface.cpp index b6a699f28577c..9a539656713cb 100644 --- a/dom/plugins/base/android/ANPSurface.cpp +++ b/dom/plugins/base/android/ANPSurface.cpp @@ -110,7 +110,7 @@ static inline void* getSurface(JNIEnv* env, jobject view) { env->DeleteLocalRef(holder); env->DeleteLocalRef(surface); - return (void*)surfacePointer; + return reinterpret_cast(uintptr_t(surfacePointer)); } static ANPBitmapFormat convertPixelFormat(int32_t format) { From 9579d5179760ae076b3badaa3dbc7d1f3c287e40 Mon Sep 17 00:00:00 2001 From: Jim Chen Date: Wed, 17 May 2017 13:06:21 -0400 Subject: [PATCH 03/37] Bug 1360321 - 3. Don't use mmap2 on Android AArch64 in mozjemalloc; r=glandium Android NDK defines SYS_mmap2 but it expands to a nonexistent symbol. mmap2 may not be supported in any case in some AArch64 kernels, so we should just use mmap. MozReview-Commit-ID: 5Vjuja5fLIL --- memory/mozjemalloc/jemalloc.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/memory/mozjemalloc/jemalloc.c b/memory/mozjemalloc/jemalloc.c index 5de4b514bcbc4..53f18ee70a50e 100644 --- a/memory/mozjemalloc/jemalloc.c +++ b/memory/mozjemalloc/jemalloc.c @@ -341,6 +341,10 @@ void *_mmap(void *addr, size_t length, int prot, int flags, } args = { addr, length, prot, flags, fd, offset }; return (void *) syscall(SYS_mmap, &args); #else +#if defined(MOZ_MEMORY_ANDROID) && defined(__aarch64__) && defined(SYS_mmap2) +/* Android NDK defines SYS_mmap2 for AArch64 despite it not supporting mmap2 */ +#undef SYS_mmap2 +#endif #ifdef SYS_mmap2 return (void *) syscall(SYS_mmap2, addr, length, prot, flags, fd, offset >> 12); From f5f1f202b1b1c97037122e38d8bb215da9471558 Mon Sep 17 00:00:00 2001 From: Jim Chen Date: Wed, 17 May 2017 13:06:22 -0400 Subject: [PATCH 04/37] Bug 1360321 - 4a. Fix printf macro mismatches in mozglue; r=froydnj r=glandium Fix printf macro mismatches where, for example, `PRIxPTR` is defined for `long` but the ELF `Addr` type is defined as `long long`. MozReview-Commit-ID: 8hXY1MpHPjS --- mozglue/linker/BaseElf.cpp | 6 +++--- mozglue/linker/CustomElf.cpp | 21 +++++++++++---------- mozglue/linker/ElfLoader.cpp | 8 ++++---- mozglue/linker/Mappable.cpp | 13 ++++++++----- 4 files changed, 26 insertions(+), 22 deletions(-) diff --git a/mozglue/linker/BaseElf.cpp b/mozglue/linker/BaseElf.cpp index 8f9e4d1bb872b..a0b16df389d74 100644 --- a/mozglue/linker/BaseElf.cpp +++ b/mozglue/linker/BaseElf.cpp @@ -185,7 +185,7 @@ LoadedElf::InitDyn(const Phdr *pt_dyn) switch (dyn->d_tag) { case DT_HASH: { - DEBUG_LOG("%s 0x%08" PRIxPTR, "DT_HASH", dyn->d_un.d_val); + DEBUG_LOG("%s 0x%08" PRIxPTR, "DT_HASH", uintptr_t(dyn->d_un.d_val)); const Elf::Word *hash_table_header = \ GetPtr(dyn->d_un.d_ptr); symnum = hash_table_header[1]; @@ -194,11 +194,11 @@ LoadedElf::InitDyn(const Phdr *pt_dyn) } break; case DT_STRTAB: - DEBUG_LOG("%s 0x%08" PRIxPTR, "DT_STRTAB", dyn->d_un.d_val); + DEBUG_LOG("%s 0x%08" PRIxPTR, "DT_STRTAB", uintptr_t(dyn->d_un.d_val)); strtab.Init(GetPtr(dyn->d_un.d_ptr)); break; case DT_SYMTAB: - DEBUG_LOG("%s 0x%08" PRIxPTR, "DT_SYMTAB", dyn->d_un.d_val); + DEBUG_LOG("%s 0x%08" PRIxPTR, "DT_SYMTAB", uintptr_t(dyn->d_un.d_val)); symtab.Init(GetPtr(dyn->d_un.d_ptr)); break; } diff --git a/mozglue/linker/CustomElf.cpp b/mozglue/linker/CustomElf.cpp index 04810ac3913ef..4828b7f73fda5 100644 --- a/mozglue/linker/CustomElf.cpp +++ b/mozglue/linker/CustomElf.cpp @@ -65,8 +65,9 @@ void debug_phdr(const char *type, const Phdr *phdr) "memsz: 0x%08" PRIxPTR ", " "offset: 0x%08" PRIxPTR ", " "flags: %c%c%c)", - type, phdr->p_vaddr, phdr->p_filesz, phdr->p_memsz, - phdr->p_offset, phdr->p_flags & PF_R ? 'r' : '-', + type, uintptr_t(phdr->p_vaddr), uintptr_t(phdr->p_filesz), + uintptr_t(phdr->p_memsz), uintptr_t(phdr->p_offset), + phdr->p_flags & PF_R ? 'r' : '-', phdr->p_flags & PF_W ? 'w' : '-', phdr->p_flags & PF_X ? 'x' : '-'); } @@ -186,7 +187,7 @@ CustomElf::Load(Mappable *mappable, const char *path, int flags) if (min_vaddr != 0) { ERROR("%s: Unsupported minimal virtual address: 0x%08" PRIxPTR, - elf->GetPath(), min_vaddr); + elf->GetPath(), uintptr_t(min_vaddr)); return nullptr; } if (!dyn) { @@ -456,7 +457,7 @@ namespace { void debug_dyn(const char *type, const Dyn *dyn) { - DEBUG_LOG("%s 0x%08" PRIxPTR, type, dyn->d_un.d_val); + DEBUG_LOG("%s 0x%08" PRIxPTR, type, uintptr_t(dyn->d_un.d_val)); } } /* anonymous namespace */ @@ -587,7 +588,7 @@ CustomElf::InitDyn(const Phdr *pt_dyn) flags &= ~DF_SYMBOLIC; if (flags) WARN("%s: unhandled flags #%" PRIxPTR" not handled", - GetPath(), flags); + GetPath(), uintptr_t(flags)); } break; case DT_SONAME: /* Should match GetName(), but doesn't matter */ @@ -610,7 +611,7 @@ CustomElf::InitDyn(const Phdr *pt_dyn) break; default: WARN("%s: dynamic header type #%" PRIxPTR" not handled", - GetPath(), dyn->d_tag); + GetPath(), uintptr_t(dyn->d_tag)); } } @@ -671,7 +672,7 @@ CustomElf::Relocate() if (symptr == nullptr) WARN("%s: Relocation to NULL @0x%08" PRIxPTR, - GetPath(), rel->r_offset); + GetPath(), uintptr_t(rel->r_offset)); /* Apply relocation */ switch (ELF_R_TYPE(rel->r_info)) { @@ -685,7 +686,7 @@ CustomElf::Relocate() break; default: ERROR("%s: Unsupported relocation type: 0x%" PRIxPTR, - GetPath(), ELF_R_TYPE(rel->r_info)); + GetPath(), uintptr_t(ELF_R_TYPE(rel->r_info))); return false; } } @@ -719,11 +720,11 @@ CustomElf::RelocateJumps() if (ELF_ST_BIND(sym.st_info) == STB_WEAK) { WARN("%s: Relocation to NULL @0x%08" PRIxPTR " for symbol \"%s\"", GetPath(), - rel->r_offset, strtab.GetStringAt(sym.st_name)); + uintptr_t(rel->r_offset), strtab.GetStringAt(sym.st_name)); } else { ERROR("%s: Relocation to NULL @0x%08" PRIxPTR " for symbol \"%s\"", GetPath(), - rel->r_offset, strtab.GetStringAt(sym.st_name)); + uintptr_t(rel->r_offset), strtab.GetStringAt(sym.st_name)); return false; } } diff --git a/mozglue/linker/ElfLoader.cpp b/mozglue/linker/ElfLoader.cpp index fd0d257bf6150..e6867d6db64fa 100644 --- a/mozglue/linker/ElfLoader.cpp +++ b/mozglue/linker/ElfLoader.cpp @@ -609,12 +609,12 @@ ElfLoader::~ElfLoader() it < list.rend(); ++it) { if ((*it)->AsSystemElf()) { DEBUG_LOG("ElfLoader::~ElfLoader(): Remaining handle for \"%s\" " - "[%d direct refs, %d refs total]", (*it)->GetPath(), - (*it)->DirectRefCount(), (*it)->refCount()); + "[%" PRIdPTR " direct refs, %" PRIdPTR " refs total]", + (*it)->GetPath(), (*it)->DirectRefCount(), (*it)->refCount()); } else { DEBUG_LOG("ElfLoader::~ElfLoader(): Unexpected remaining handle for \"%s\" " - "[%d direct refs, %d refs total]", (*it)->GetPath(), - (*it)->DirectRefCount(), (*it)->refCount()); + "[%" PRIdPTR " direct refs, %" PRIdPTR " refs total]", + (*it)->GetPath(), (*it)->DirectRefCount(), (*it)->refCount()); /* Not removing, since it could have references to other libraries, * destroying them as a side effect, and possibly leaving dangling * pointers in the handle list we're scanning */ diff --git a/mozglue/linker/Mappable.cpp b/mozglue/linker/Mappable.cpp index e43407c066098..fcad1d0bd7ed5 100644 --- a/mozglue/linker/Mappable.cpp +++ b/mozglue/linker/Mappable.cpp @@ -13,6 +13,7 @@ #include "Mappable.h" +#include "mozilla/IntegerPrintfMacros.h" #include "mozilla/SizePrintfMacros.h" #include "mozilla/UniquePtr.h" @@ -212,7 +213,8 @@ MappableExtractFile::Create(const char *name, Zip *zip, Zip::Stream *stream) ERROR("Couldn't initialize XZ decoder"); return nullptr; } - DEBUG_LOG("XZStream created, compressed=%u, uncompressed=%u", + DEBUG_LOG("XZStream created, compressed=%" PRIuPTR + ", uncompressed=%" PRIuPTR, xzStream.Size(), xzStream.UncompressedSize()); if (ftruncate(fd, xzStream.UncompressedSize()) == -1) { @@ -226,7 +228,7 @@ MappableExtractFile::Create(const char *name, Zip *zip, Zip::Stream *stream) return nullptr; } const size_t written = xzStream.Decode(buffer, buffer.GetLength()); - DEBUG_LOG("XZStream decoded %u", written); + DEBUG_LOG("XZStream decoded %" PRIuPTR, written); if (written != buffer.GetLength()) { ERROR("Error decoding XZ file %s", file.get()); return nullptr; @@ -299,7 +301,8 @@ class _MappableBuffer: public MappedPtr if (buf != MAP_FAILED) { ::mmap(AlignedEndPtr(reinterpret_cast(buf) + length, PAGE_SIZE), PAGE_SIZE, PROT_NONE, MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); - DEBUG_LOG("Decompression buffer of size 0x%x in ashmem \"%s\", mapped @%p", + DEBUG_LOG("Decompression buffer of size 0x%" PRIxPTR + " in ashmem \"%s\", mapped @%p", length, str, buf); return new _MappableBuffer(fd.forget(), buf, length); } @@ -319,8 +322,8 @@ class _MappableBuffer: public MappedPtr return nullptr; } - DEBUG_LOG("Decompression buffer of size 0x%x in ashmem \"%s\", mapped @%p", - length, str, actual_buf); + DEBUG_LOG("Decompression buffer of size 0x%" PRIxPTR + " in ashmem \"%s\", mapped @%p", length, str, actual_buf); return new _MappableBuffer(fd.forget(), actual_buf, length); } #else From e1831b091ff26e7e537a79e08e8a4e7bcfe5505e Mon Sep 17 00:00:00 2001 From: Jim Chen Date: Wed, 17 May 2017 13:06:22 -0400 Subject: [PATCH 05/37] Bug 1360321 - 4b. Add relocation macros for AArch64; r=froydnj r=glandium Add relocation macros for AArch64 in the custom linker. MozReview-Commit-ID: 4TKtVJdq0is --- mozglue/linker/Elfxx.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/mozglue/linker/Elfxx.h b/mozglue/linker/Elfxx.h index b21a893365cbb..d60dea20692ce 100644 --- a/mozglue/linker/Elfxx.h +++ b/mozglue/linker/Elfxx.h @@ -110,6 +110,18 @@ #define STR_RELOC(n) "DT_REL" # n #define Reloc Rel +#elif defined(__aarch64__) +#define ELFMACHINE EM_AARCH64 + +#define R_ABS R_AARCH64_ABS64 +#define R_GLOB_DAT R_AARCH64_GLOB_DAT +#define R_JMP_SLOT R_AARCH64_JUMP_SLOT +#define R_RELATIVE R_AARCH64_RELATIVE +#define RELOC(n) DT_RELA ## n +#define UNSUPPORTED_RELOC(n) DT_REL ## n +#define STR_RELOC(n) "DT_RELA" # n +#define Reloc Rela + #else #error Unknown ELF machine type #endif From 8057a2581349357a0a31441edea2f5a27c87298d Mon Sep 17 00:00:00 2001 From: Jim Chen Date: Wed, 17 May 2017 13:06:22 -0400 Subject: [PATCH 06/37] Bug 1360321 - 4c. Add Divert case for AArch64; r=froydnj r=glandium Fill in AArch64 trampoline for Divert(). Even though we're not using on-demand decompression anymore, I added the AArch64 cases for completeness. MozReview-Commit-ID: D91KhHiDo7S --- mozglue/linker/ElfLoader.cpp | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/mozglue/linker/ElfLoader.cpp b/mozglue/linker/ElfLoader.cpp index e6867d6db64fa..5e945f9fc9954 100644 --- a/mozglue/linker/ElfLoader.cpp +++ b/mozglue/linker/ElfLoader.cpp @@ -970,7 +970,7 @@ ElfLoader::DebuggerHelper::Remove(ElfLoader::link_map *map) dbg->r_brk(); } -#if defined(ANDROID) +#if defined(ANDROID) && defined(__NR_sigaction) /* As some system libraries may be calling signal() or sigaction() to * set a SIGSEGV handler, effectively breaking MappableSeekableZStream, * or worse, restore our SIGSEGV handler with wrong flags (which using @@ -1016,8 +1016,9 @@ Divert(T func, T new_func) *reinterpret_cast(addr + 1) = reinterpret_cast(new_func) - addr - 5; // target displacement return true; -#elif defined(__arm__) +#elif defined(__arm__) || defined(__aarch64__) const unsigned char trampoline[] = { +# ifdef __arm__ // .thumb 0x46, 0x04, // nop 0x78, 0x47, // bx pc @@ -1025,8 +1026,15 @@ Divert(T func, T new_func) // .arm 0x04, 0xf0, 0x1f, 0xe5, // ldr pc, [pc, #-4] // .word +# else // __aarch64__ + 0x50, 0x00, 0x00, 0x58, // ldr x16, [pc, #8] ; x16 (aka ip0) is the first + 0x00, 0x02, 0x1f, 0xd6, // br x16 ; intra-procedure-call + // .word ; scratch register. + // .word +# endif }; const unsigned char *start; +# ifdef __arm__ if (addr & 0x01) { /* Function is thumb, the actual address of the code is without the * least significant bit. */ @@ -1040,12 +1048,16 @@ Divert(T func, T new_func) /* Function is arm, we only need the arm part of the trampoline */ start = trampoline + 6; } +# else // __aarch64__ + start = trampoline; +#endif size_t len = sizeof(trampoline) - (start - trampoline); EnsureWritable w(reinterpret_cast(addr), len + sizeof(void *)); memcpy(reinterpret_cast(addr), start, len); *reinterpret_cast(addr + len) = FunctionPtr(new_func); - cacheflush(addr, addr + len + sizeof(void *), 0); + __builtin___clear_cache(reinterpret_cast(addr), + reinterpret_cast(addr + len + sizeof(void *))); return true; #else return false; From 7752e253ee359268c11b270f0188e4420c8dcfaf Mon Sep 17 00:00:00 2001 From: Jim Chen Date: Wed, 17 May 2017 13:06:22 -0400 Subject: [PATCH 07/37] Bug 1360321 - 4d. Define mmap ordering for AArch64; r=froydnj r=glandium Specify mmap address ordering for AArch64 so we properly allocate buffer pages. MozReview-Commit-ID: 4vGztSsAaXm --- mozglue/linker/Mappable.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/mozglue/linker/Mappable.cpp b/mozglue/linker/Mappable.cpp index fcad1d0bd7ed5..00b011c618141 100644 --- a/mozglue/linker/Mappable.cpp +++ b/mozglue/linker/Mappable.cpp @@ -296,6 +296,7 @@ class _MappableBuffer: public MappedPtr * depending on how mappings grow in the address space. */ #if defined(__arm__) + // Address increases on ARM. void *buf = ::mmap(nullptr, length + PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (buf != MAP_FAILED) { @@ -306,7 +307,8 @@ class _MappableBuffer: public MappedPtr length, str, buf); return new _MappableBuffer(fd.forget(), buf, length); } -#elif defined(__i386__) +#elif defined(__i386__) || defined(__aarch64__) + // Address decreases on x86 and AArch64. size_t anon_mapping_length = length + PAGE_SIZE; void *buf = ::mmap(nullptr, anon_mapping_length, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); @@ -371,7 +373,7 @@ class _MappableBuffer: public MappedPtr /* Free the additional page we allocated. See _MappableBuffer::Create */ #if defined(__arm__) ::munmap(AlignedEndPtr(*this + GetLength(), PAGE_SIZE), PAGE_SIZE); -#elif defined(__i386__) +#elif defined(__i386__) || defined(__aarch64__) ::munmap(*this - PAGE_SIZE, GetLength() + PAGE_SIZE); #else #error need to add a case for your CPU From 8dd6064fc9b408a52948d633172d206ec9a36a85 Mon Sep 17 00:00:00 2001 From: Jim Chen Date: Wed, 17 May 2017 13:06:22 -0400 Subject: [PATCH 08/37] Bug 1360321 - 4e. Fix std::min type error; r=froydnj r=glandium Specify the same type for std::min arguments to avoid the error. MozReview-Commit-ID: C7yHu7c1s34 --- mozglue/linker/XZStream.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mozglue/linker/XZStream.cpp b/mozglue/linker/XZStream.cpp index bd71655f52c7d..30f3a32caa680 100644 --- a/mozglue/linker/XZStream.cpp +++ b/mozglue/linker/XZStream.cpp @@ -18,7 +18,7 @@ ParseVarLenInt(const uint8_t* aBuf, size_t aBufSize, uint64_t* aValue) if (!aBufSize) { return 0; } - aBufSize = std::min(9u, aBufSize); + aBufSize = std::min(size_t(9), aBufSize); *aValue = aBuf[0] & 0x7F; size_t i = 0; From 27d618738ab989ddafe9e48672b53a1d320f3adc Mon Sep 17 00:00:00 2001 From: Jim Chen Date: Wed, 17 May 2017 13:06:23 -0400 Subject: [PATCH 09/37] Bug 1360321 - 5. Fix warnings in widget; r=snorp * Fix warnings using putenv with string literals. * Fix a pritnf format warning. MozReview-Commit-ID: B53sUjGTg7z --- widget/android/AndroidBridge.cpp | 2 +- widget/android/nsWindow.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/widget/android/AndroidBridge.cpp b/widget/android/AndroidBridge.cpp index 1fd218efbf3d5..f758956586807 100644 --- a/widget/android/AndroidBridge.cpp +++ b/widget/android/AndroidBridge.cpp @@ -129,7 +129,7 @@ AndroidBridge::ConstructBridge() * to call dlclose() while we're already inside dlclose(). * Conveniently, NSS has an env var that can prevent it from unloading. */ - putenv("NSS_DISABLE_UNLOAD=1"); + putenv(const_cast("NSS_DISABLE_UNLOAD=1")); MOZ_ASSERT(!sBridge); sBridge = new AndroidBridge(); diff --git a/widget/android/nsWindow.cpp b/widget/android/nsWindow.cpp index dad1dada730b5..112453b94a901 100644 --- a/widget/android/nsWindow.cpp +++ b/widget/android/nsWindow.cpp @@ -1432,8 +1432,8 @@ nsWindow::LogWindow(nsWindow *win, int index, int indent) #if defined(DEBUG) || defined(FORCE_ALOG) char spaces[] = " "; spaces[indent < 20 ? indent : 20] = 0; - ALOG("%s [% 2d] 0x%08x [parent 0x%08x] [% 3d,% 3dx% 3d,% 3d] vis %d type %d", - spaces, index, (intptr_t)win, (intptr_t)win->mParent, + ALOG("%s [% 2d] 0x%p [parent 0x%p] [% 3d,% 3dx% 3d,% 3d] vis %d type %d", + spaces, index, win, win->mParent, win->mBounds.x, win->mBounds.y, win->mBounds.width, win->mBounds.height, win->mIsVisible, win->mWindowType); From 14dbe23a6c6687275e27ca58788dd1f03ae7c689 Mon Sep 17 00:00:00 2001 From: Jim Chen Date: Wed, 17 May 2017 13:06:23 -0400 Subject: [PATCH 10/37] Bug 1360321 - 6. Add AArch64 support in xpcom; r=froydnj Add breakpoint support for AArch64, and fix a scanf format specifier warning. Also fix an #if line in xptcinvoke_arm.cpp to work as intended. MozReview-Commit-ID: BSjYVD8Zq0t --- xpcom/base/nsDebugImpl.cpp | 4 +++- xpcom/base/nsMemoryImpl.cpp | 2 +- xpcom/reflect/xptcall/md/unix/xptcinvoke_arm.cpp | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/xpcom/base/nsDebugImpl.cpp b/xpcom/base/nsDebugImpl.cpp index 94b8e3ebb129a..e6bb623c258be 100644 --- a/xpcom/base/nsDebugImpl.cpp +++ b/xpcom/base/nsDebugImpl.cpp @@ -471,6 +471,8 @@ RealBreak() ".object_arch armv4t\n" #endif "BKPT #0"); +#elif defined(__aarch64__) + asm("brk #0"); #elif defined(SOLARIS) #if defined(__i386__) || defined(__i386) || defined(__x86_64__) asm("int $3"); @@ -549,7 +551,7 @@ Break(const char* aMsg) RealBreak(); #elif defined(__GNUC__) && (defined(__i386__) || defined(__i386) || defined(__x86_64__)) RealBreak(); -#elif defined(__arm__) +#elif defined(__arm__) || defined(__aarch64__) RealBreak(); #elif defined(SOLARIS) RealBreak(); diff --git a/xpcom/base/nsMemoryImpl.cpp b/xpcom/base/nsMemoryImpl.cpp index 1d1576fbd42e4..5e966fe4ee304 100644 --- a/xpcom/base/nsMemoryImpl.cpp +++ b/xpcom/base/nsMemoryImpl.cpp @@ -49,7 +49,7 @@ nsMemoryImpl::IsLowMemoryPlatform(bool* aResult) return NS_OK; } uint64_t mem = 0; - int rv = fscanf(fd, "MemTotal: %llu kB", &mem); + int rv = fscanf(fd, "MemTotal: %" PRIu64 " kB", &mem); if (fclose(fd)) { return NS_OK; } diff --git a/xpcom/reflect/xptcall/md/unix/xptcinvoke_arm.cpp b/xpcom/reflect/xptcall/md/unix/xptcinvoke_arm.cpp index 4cd5eb47d8dd1..1760eb7cd2b3c 100644 --- a/xpcom/reflect/xptcall/md/unix/xptcinvoke_arm.cpp +++ b/xpcom/reflect/xptcall/md/unix/xptcinvoke_arm.cpp @@ -9,7 +9,7 @@ #include "mozilla/Compiler.h" -#if !defined(__arm__) && !(defined(LINUX) || defined(ANDROID) || defined(XP_IOS)) +#if !defined(__arm__) || !(defined(LINUX) || defined(ANDROID) || defined(XP_IOS)) #error "This code is for Linux/iOS ARM only. Check that it works on your system, too.\nBeware that this code is highly compiler dependent." #endif From 65efbc986a1b01fefb55802d4575867bd856eed5 Mon Sep 17 00:00:00 2001 From: Jim Chen Date: Wed, 17 May 2017 13:06:23 -0400 Subject: [PATCH 11/37] Bug 1360321 - 7. Add AArch64 check to Fennec hardware checks; r=snorp Add checks for "arm64-v8a" when we check for supported platforms. MozReview-Commit-ID: IHklaZ7Q6BD --- mobile/android/base/AppConstants.java.in | 2 +- mobile/android/geckoview/BuildConfig.java.in | 2 +- .../java/org/mozilla/gecko/util/HardwareUtils.java | 10 +++++++++- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/mobile/android/base/AppConstants.java.in b/mobile/android/base/AppConstants.java.in index 3e180bdec1256..959787fa6bbda 100644 --- a/mobile/android/base/AppConstants.java.in +++ b/mobile/android/base/AppConstants.java.in @@ -291,7 +291,7 @@ public class AppConstants { //#endif /** - * Target CPU architecture: "armeabi-v7a", "x86, "mips", .. + * Target CPU architecture: "armeabi-v7a", "arm64-v8a", "x86", "mips", .. */ public static final String ANDROID_CPU_ARCH = "@ANDROID_CPU_ARCH@"; diff --git a/mobile/android/geckoview/BuildConfig.java.in b/mobile/android/geckoview/BuildConfig.java.in index 297853f1b2d15..809d3157b3a6c 100644 --- a/mobile/android/geckoview/BuildConfig.java.in +++ b/mobile/android/geckoview/BuildConfig.java.in @@ -51,7 +51,7 @@ public class BuildConfig { MOZ_APP_VERSION; /** - * Target CPU architecture: "armeabi-v7a", "x86, "mips", .. + * Target CPU architecture: "armeabi-v7a", "arm64-v8a", "x86", "mips", .. */ public static final String ANDROID_CPU_ARCH = "@ANDROID_CPU_ARCH@"; } diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/HardwareUtils.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/HardwareUtils.java index ea08d89dcc6e2..dc55089b0ed11 100644 --- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/HardwareUtils.java +++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/HardwareUtils.java @@ -85,6 +85,11 @@ public static boolean isARMSystem() { return Build.CPU_ABI != null && Build.CPU_ABI.equals("armeabi-v7a"); } + public static boolean isARM64System() { + // 64-bit support was introduced in 21. + return Build.VERSION.SDK_INT >= 21 && "arm64-v8a".equals(Build.SUPPORTED_ABIS[0]); + } + public static boolean isX86System() { if (Build.CPU_ABI != null && Build.CPU_ABI.equals("x86")) { return true; @@ -122,8 +127,10 @@ public static boolean isSupportedSystem() { // See http://developer.android.com/ndk/guides/abis.html final boolean isSystemX86 = isX86System(); final boolean isSystemARM = !isSystemX86 && isARMSystem(); + final boolean isSystemARM64 = isARM64System(); boolean isAppARM = BuildConfig.ANDROID_CPU_ARCH.startsWith("armeabi-v7a"); + boolean isAppARM64 = BuildConfig.ANDROID_CPU_ARCH.startsWith("arm64-v8a"); boolean isAppX86 = BuildConfig.ANDROID_CPU_ARCH.startsWith("x86"); // Only reject known incompatible ABIs. Better safe than sorry. @@ -131,7 +138,8 @@ public static boolean isSupportedSystem() { return false; } - if ((isSystemX86 && isAppX86) || (isSystemARM && isAppARM)) { + if ((isSystemX86 && isAppX86) || (isSystemARM && isAppARM) || + (isSystemARM64 && (isAppARM64 || isAppARM))) { return true; } From a2dff5ad917b5f76ee04f7801cc1b24b9f3c1659 Mon Sep 17 00:00:00 2001 From: Jim Chen Date: Wed, 17 May 2017 13:06:23 -0400 Subject: [PATCH 12/37] Bug 1360321 - 10. Fix opt build warnings in VIXL; r=me Fix an unused variable warning for `visitor` because it's only used in the assertion macro. Fix several no-return-value errors because the compiler cannot assume the VIXL_UNREACHABLE() macro is actually unreachable. r=me for trivial patch. MozReview-Commit-ID: 13IlMyUsXUN --- js/src/jit/arm64/vixl/Decoder-vixl.h | 2 ++ js/src/jit/arm64/vixl/Globals-vixl.h | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/js/src/jit/arm64/vixl/Decoder-vixl.h b/js/src/jit/arm64/vixl/Decoder-vixl.h index 95dd589e8ab09..742c6f954c72f 100644 --- a/js/src/jit/arm64/vixl/Decoder-vixl.h +++ b/js/src/jit/arm64/vixl/Decoder-vixl.h @@ -151,9 +151,11 @@ class Decoder { // Top-level wrappers around the actual decoding function. void Decode(const Instruction* instr) { +#ifdef DEBUG for (auto visitor : visitors_) { VIXL_ASSERT(visitor->IsConstVisitor()); } +#endif DecodeInstruction(instr); } void Decode(Instruction* instr) { diff --git a/js/src/jit/arm64/vixl/Globals-vixl.h b/js/src/jit/arm64/vixl/Globals-vixl.h index 8a7418eb8c474..39d9c1d3f37dc 100644 --- a/js/src/jit/arm64/vixl/Globals-vixl.h +++ b/js/src/jit/arm64/vixl/Globals-vixl.h @@ -76,7 +76,7 @@ const int MBytes = 1024 * KBytes; #define VIXL_ASSERT(condition) ((void) 0) #define VIXL_CHECK(condition) ((void) 0) #define VIXL_UNIMPLEMENTED() ((void) 0) - #define VIXL_UNREACHABLE() ((void) 0) + #define VIXL_UNREACHABLE() MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE() #endif // This is not as powerful as template based assertions, but it is simple. // It assumes that the descriptions are unique. If this starts being a problem, From 6ed039fa49b01e6c028cfe0942ef8b5081e54ed8 Mon Sep 17 00:00:00 2001 From: Jim Chen Date: Wed, 17 May 2017 13:06:23 -0400 Subject: [PATCH 13/37] Bug 1360321 - 11. Include AArch64 in nsWrapperCache 64-bit check; r=me Include AArch64 in the 64-bit architecture check in nsWrapperCache.h. Otherwise AArch64 builds fail with a build error. r=me for trivial patch. MozReview-Commit-ID: 60CXTNBbS2x --- dom/base/nsWrapperCache.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dom/base/nsWrapperCache.h b/dom/base/nsWrapperCache.h index a101092e22b5b..b00d7aceb5c84 100644 --- a/dom/base/nsWrapperCache.h +++ b/dom/base/nsWrapperCache.h @@ -41,7 +41,7 @@ class nsWindowRoot; // This may waste space for some other nsWrapperCache-derived objects that have // a 32-bit field as their first member, but those objects are unlikely to be as // numerous or performance-critical as DOM nodes. -#if defined(_M_X64) || defined(__x86_64__) +#if defined(_M_X64) || defined(__x86_64__) || defined(__aarch64__) static_assert(sizeof(void*) == 8, "These architectures should be 64-bit"); #define BOOL_FLAGS_ON_WRAPPER_CACHE #else From 34f6fb7d5b1e18bb03d58ea4f22c4ac73e3ce95b Mon Sep 17 00:00:00 2001 From: Dale Harvey Date: Wed, 17 May 2017 19:02:52 +0100 Subject: [PATCH 14/37] Bug 1365213 - Fix location and search bar height. r=dao --- browser/themes/osx/browser.css | 2 +- browser/themes/shared/location-search-bar.inc.css | 1 + browser/themes/windows/browser.css | 2 ++ 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/browser/themes/osx/browser.css b/browser/themes/osx/browser.css index c869ca1cb0d2d..906c1b8883775 100644 --- a/browser/themes/osx/browser.css +++ b/browser/themes/osx/browser.css @@ -538,7 +538,7 @@ toolbarpaletteitem[place="palette"] > #personal-bookmarks > #bookmarks-toolbar-p #urlbar, .searchbar-textbox { - font: icon; + font-size: 1.25em; } %else diff --git a/browser/themes/shared/location-search-bar.inc.css b/browser/themes/shared/location-search-bar.inc.css index 167b460aaf1b5..d3feb2a01f6cf 100644 --- a/browser/themes/shared/location-search-bar.inc.css +++ b/browser/themes/shared/location-search-bar.inc.css @@ -12,6 +12,7 @@ box-shadow: 0 1px 4px hsla(0, 0%, 0%, .05); padding: 0; margin: 0 2px; + min-height: 30px; } #urlbar:-moz-lwtheme, diff --git a/browser/themes/windows/browser.css b/browser/themes/windows/browser.css index 1d249d9c79bd0..d2cf1ea178075 100644 --- a/browser/themes/windows/browser.css +++ b/browser/themes/windows/browser.css @@ -752,7 +752,9 @@ toolbar[brighttext] #close-button { #urlbar, .searchbar-textbox { font-size: 1.15em; +%ifndef MOZ_PHOTON_THEME min-height: 28px; +%endif } :root { From dfe1e1a0cdb241dc69c028bcd0ef79e465690eb6 Mon Sep 17 00:00:00 2001 From: Mihai Tabara Date: Wed, 17 May 2017 13:36:18 +0100 Subject: [PATCH 15/37] Bug 1364225 - in-tree beetmover to take product in a generic way via configs. r=rail a=release DONTBUILD MozReview-Commit-ID: 23iQ4leB1Ob --HG-- extra : rebase_source : a7c326fe897b7d858f43f565950472f079c6dd7e extra : intermediate-source : d150c65de6718e508b331ca9e49a80bd40159f0e extra : source : 896085e262c812aede8382e2cf45963ab29f9c4e --- testing/mozharness/scripts/release/beet_mover.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/testing/mozharness/scripts/release/beet_mover.py b/testing/mozharness/scripts/release/beet_mover.py index 2778e76e695b2..4ac70c04e0718 100755 --- a/testing/mozharness/scripts/release/beet_mover.py +++ b/testing/mozharness/scripts/release/beet_mover.py @@ -76,6 +76,10 @@ def get_hash(content, hash_type="md5"): "dest": "bucket", "help": "s3 bucket to move beets to.", }], + [["--product"], { + "dest": "product", + "help": "product for which artifacts are beetmoved", + }], [["--exclude"], { "dest": "excludes", "action": "append", @@ -151,7 +155,6 @@ def __init__(self, aws_creds): "mar", ], "virtualenv_path": "venv", - 'product': 'firefox', }, } #todo do excludes need to be configured via command line for specific builds? From 496252c6859c3f9fa2ed90d5ff3cadb7a150897d Mon Sep 17 00:00:00 2001 From: Steve Fink Date: Fri, 12 May 2017 12:10:56 -0700 Subject: [PATCH 16/37] Bug 1364547 - Add number of slices to major GC summary, r=jonco --HG-- extra : rebase_source : c7cdf4ef944c4bc227650e6027a59907bbf4fd21 --- js/src/gc/Statistics.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/js/src/gc/Statistics.cpp b/js/src/gc/Statistics.cpp index 7c6e025e4f36d..03d719d94c76e 100644 --- a/js/src/gc/Statistics.cpp +++ b/js/src/gc/Statistics.cpp @@ -581,6 +581,7 @@ Statistics::formatJsonDescription(uint64_t timestamp, JSONPrinter& json) const json.property("total_compartments", zoneStats.compartmentCount); json.property("minor_gcs", counts[STAT_MINOR_GC]); json.property("store_buffer_overflows", counts[STAT_STOREBUFFER_OVERFLOW]); + json.property("slices", slices_.length()); const double mmu20 = computeMMU(TimeDuration::FromMilliseconds(20)); const double mmu50 = computeMMU(TimeDuration::FromMilliseconds(50)); From 51871e1507226e54135bc72276cff40273db8a27 Mon Sep 17 00:00:00 2001 From: Steve Fink Date: Mon, 15 May 2017 08:06:24 -0700 Subject: [PATCH 17/37] Bug 1364547 - Call the slice end callback for every slice, r=jonco Currently, the final slice of an incremental GC only gets a GC_CYCLE_END callback, not a GC_SLICE_END callback. So if you are doing anything that expects to see all of the slices, you will be missing one. Simplify the setup so that every GC is bracketed with CYCLE_BEGIN/END, and every slice is bracketed with SLICE_BEGIN/END, treating a nonincremental as a GC with a single slice (which it is for everything else.) --HG-- extra : rebase_source : 8e21300819d517b3e35de14930f53b3ab737a44e --- js/public/GCAPI.h | 10 +++------- js/src/gc/Statistics.cpp | 22 ++++++++++++++-------- js/src/jsapi-tests/testGCHooks.cpp | 11 +++++++---- js/xpconnect/src/XPCJSRuntime.cpp | 3 +-- 4 files changed, 25 insertions(+), 21 deletions(-) diff --git a/js/public/GCAPI.h b/js/public/GCAPI.h index e9da6cb65c409..c3228ce2cc752 100644 --- a/js/public/GCAPI.h +++ b/js/public/GCAPI.h @@ -319,13 +319,9 @@ class GarbageCollectionEvent enum GCProgress { /* - * During non-incremental GC, the GC is bracketed by JSGC_CYCLE_BEGIN/END - * callbacks. During an incremental GC, the sequence of callbacks is as - * follows: - * JSGC_CYCLE_BEGIN, JSGC_SLICE_END (first slice) - * JSGC_SLICE_BEGIN, JSGC_SLICE_END (second slice) - * ... - * JSGC_SLICE_BEGIN, JSGC_CYCLE_END (last slice) + * During GC, the GC is bracketed by GC_CYCLE_BEGIN/END callbacks. Each + * slice between those (whether an incremental or the sole non-incremental + * slice) is bracketed by GC_SLICE_BEGIN/GC_SLICE_END. */ GC_CYCLE_BEGIN, diff --git a/js/src/gc/Statistics.cpp b/js/src/gc/Statistics.cpp index 03d719d94c76e..b0dc1bd38f4e7 100644 --- a/js/src/gc/Statistics.cpp +++ b/js/src/gc/Statistics.cpp @@ -959,10 +959,13 @@ Statistics::beginSlice(const ZoneGCStats& zoneStats, JSGCInvocationKind gckind, // Slice callbacks should only fire for the outermost level. bool wasFullGC = zoneStats.isCollectingAllZones(); - if (sliceCallback) - (*sliceCallback)(TlsContext.get(), - first ? JS::GC_CYCLE_BEGIN : JS::GC_SLICE_BEGIN, - JS::GCDescription(!wasFullGC, gckind, reason)); + if (sliceCallback) { + JSContext* cx = TlsContext.get(); + JS::GCDescription desc(!wasFullGC, gckind, reason); + if (first) + (*sliceCallback)(cx, JS::GC_CYCLE_BEGIN, desc); + (*sliceCallback)(cx, JS::GC_SLICE_BEGIN, desc); + } } void @@ -1006,10 +1009,13 @@ Statistics::endSlice() // Slice callbacks should only fire for the outermost level. if (!aborted) { bool wasFullGC = zoneStats.isCollectingAllZones(); - if (sliceCallback) - (*sliceCallback)(TlsContext.get(), - last ? JS::GC_CYCLE_END : JS::GC_SLICE_END, - JS::GCDescription(!wasFullGC, gckind, slices_.back().reason)); + if (sliceCallback) { + JSContext* cx = TlsContext.get(); + JS::GCDescription desc(!wasFullGC, gckind, slices_.back().reason); + (*sliceCallback)(cx, JS::GC_SLICE_END, desc); + if (last) + (*sliceCallback)(cx, JS::GC_CYCLE_END, desc); + } } // Do this after the slice callback since it uses these values. diff --git a/js/src/jsapi-tests/testGCHooks.cpp b/js/src/jsapi-tests/testGCHooks.cpp index 9dcaef23e9cef..d169f640297c7 100644 --- a/js/src/jsapi-tests/testGCHooks.cpp +++ b/js/src/jsapi-tests/testGCHooks.cpp @@ -14,12 +14,15 @@ static unsigned gSliceCallbackCount = 0; static void NonIncrementalGCSliceCallback(JSContext* cx, JS::GCProgress progress, const JS::GCDescription& desc) { - ++gSliceCallbackCount; - MOZ_RELEASE_ASSERT(progress == JS::GC_CYCLE_BEGIN || progress == JS::GC_CYCLE_END); + using namespace JS; + static GCProgress expect[] = + { GC_CYCLE_BEGIN, GC_SLICE_BEGIN, GC_SLICE_END, GC_CYCLE_END }; + + MOZ_RELEASE_ASSERT(progress == expect[gSliceCallbackCount++]); MOZ_RELEASE_ASSERT(desc.isZone_ == false); MOZ_RELEASE_ASSERT(desc.invocationKind_ == GC_NORMAL); MOZ_RELEASE_ASSERT(desc.reason_ == JS::gcreason::API); - if (progress == JS::GC_CYCLE_END) { + if (progress == GC_CYCLE_END) { mozilla::UniquePtr summary(desc.formatSummaryMessage(cx)); mozilla::UniquePtr message(desc.formatSliceMessage(cx)); mozilla::UniquePtr json(desc.formatJSON(cx, 0)); @@ -31,7 +34,7 @@ BEGIN_TEST(testGCSliceCallback) JS::SetGCSliceCallback(cx, NonIncrementalGCSliceCallback); JS_GC(cx); JS::SetGCSliceCallback(cx, nullptr); - CHECK(gSliceCallbackCount == 2); + CHECK(gSliceCallbackCount == 4); return true; } END_TEST(testGCSliceCallback) diff --git a/js/xpconnect/src/XPCJSRuntime.cpp b/js/xpconnect/src/XPCJSRuntime.cpp index 3e847016df0df..11ede346f5ad7 100644 --- a/js/xpconnect/src/XPCJSRuntime.cpp +++ b/js/xpconnect/src/XPCJSRuntime.cpp @@ -756,8 +756,7 @@ XPCJSRuntime::GCSliceCallback(JSContext* cx, return; #ifdef MOZ_CRASHREPORTER - CrashReporter::SetGarbageCollecting(progress == JS::GC_CYCLE_BEGIN || - progress == JS::GC_SLICE_BEGIN); + CrashReporter::SetGarbageCollecting(progress == JS::GC_CYCLE_BEGIN); #endif if (self->mPrevGCSliceCallback) From 2c8f69a305d0cc714f46b60aaa52a176ad3894b2 Mon Sep 17 00:00:00 2001 From: Steve Fink Date: Fri, 12 May 2017 16:46:43 -0700 Subject: [PATCH 18/37] Bug 1364547 - Tell Gecko whether a slice is a final slice, r=jonco,mccr8 --HG-- extra : rebase_source : ea028988b3515cdc441af8f7c2c1ba0794482494 --- dom/base/nsJSEnvironment.cpp | 4 ++-- js/public/GCAPI.h | 5 +++-- js/src/gc/Statistics.cpp | 4 ++-- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/dom/base/nsJSEnvironment.cpp b/dom/base/nsJSEnvironment.cpp index f6a074859ba70..93fb5dcbe4258 100644 --- a/dom/base/nsJSEnvironment.cpp +++ b/dom/base/nsJSEnvironment.cpp @@ -2211,9 +2211,9 @@ DOMGCSliceCallback(JSContext* aCx, JS::GCProgress aProgress, const JS::GCDescrip case JS::GC_SLICE_END: - // The GC has more work to do, so schedule another GC slice. + // Schedule another GC slice if the GC has more work to do. nsJSContext::KillInterSliceGCTimer(); - if (!sShuttingDown) { + if (!sShuttingDown && !aDesc.isComplete_) { CallCreateInstance("@mozilla.org/timer;1", &sInterSliceGCTimer); sInterSliceGCTimer->SetTarget(SystemGroup::EventTargetFor(TaskCategory::GarbageCollection)); sInterSliceGCTimer->InitWithNamedFuncCallback(InterSliceGCTimerFired, diff --git a/js/public/GCAPI.h b/js/public/GCAPI.h index c3228ce2cc752..b7fb6791d1cd4 100644 --- a/js/public/GCAPI.h +++ b/js/public/GCAPI.h @@ -332,11 +332,12 @@ enum GCProgress { struct JS_PUBLIC_API(GCDescription) { bool isZone_; + bool isComplete_; JSGCInvocationKind invocationKind_; gcreason::Reason reason_; - GCDescription(bool isZone, JSGCInvocationKind kind, gcreason::Reason reason) - : isZone_(isZone), invocationKind_(kind), reason_(reason) {} + GCDescription(bool isZone, bool isComplete, JSGCInvocationKind kind, gcreason::Reason reason) + : isZone_(isZone), isComplete_(isComplete), invocationKind_(kind), reason_(reason) {} char16_t* formatSliceMessage(JSContext* cx) const; char16_t* formatSummaryMessage(JSContext* cx) const; diff --git a/js/src/gc/Statistics.cpp b/js/src/gc/Statistics.cpp index b0dc1bd38f4e7..37c0d32aa8377 100644 --- a/js/src/gc/Statistics.cpp +++ b/js/src/gc/Statistics.cpp @@ -961,7 +961,7 @@ Statistics::beginSlice(const ZoneGCStats& zoneStats, JSGCInvocationKind gckind, bool wasFullGC = zoneStats.isCollectingAllZones(); if (sliceCallback) { JSContext* cx = TlsContext.get(); - JS::GCDescription desc(!wasFullGC, gckind, reason); + JS::GCDescription desc(!wasFullGC, false, gckind, reason); if (first) (*sliceCallback)(cx, JS::GC_CYCLE_BEGIN, desc); (*sliceCallback)(cx, JS::GC_SLICE_BEGIN, desc); @@ -1011,7 +1011,7 @@ Statistics::endSlice() bool wasFullGC = zoneStats.isCollectingAllZones(); if (sliceCallback) { JSContext* cx = TlsContext.get(); - JS::GCDescription desc(!wasFullGC, gckind, slices_.back().reason); + JS::GCDescription desc(!wasFullGC, last, gckind, slices_.back().reason); (*sliceCallback)(cx, JS::GC_SLICE_END, desc); if (last) (*sliceCallback)(cx, JS::GC_CYCLE_END, desc); From fddeb6a448ad2422484b810aa2933faf00b07c64 Mon Sep 17 00:00:00 2001 From: Steve Fink Date: Fri, 12 May 2017 12:11:48 -0700 Subject: [PATCH 19/37] Bug 1364547 - Do not report untracked timings for nursery collections, r=jonco --HG-- extra : rebase_source : d30182c2dc78e129bc8559695c3ff7ab78208065 --- js/src/gc/Nursery.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/js/src/gc/Nursery.cpp b/js/src/gc/Nursery.cpp index 98027605b4713..39efc80eddc0d 100644 --- a/js/src/gc/Nursery.cpp +++ b/js/src/gc/Nursery.cpp @@ -508,8 +508,12 @@ FOR_EACH_NURSERY_PROFILE_TIME(EXTRACT_NAME) "" }; size_t i = 0; - for (auto time : profileDurations_) - json.property(names[i++], time, json.MICROSECONDS); + if (trackTimings_) { + for (auto time : profileDurations_) + json.property(names[i++], time, json.MICROSECONDS); + } else { + json.property(names[0], *profileDurations_.begin(), json.MICROSECONDS); + } json.endObject(); } From 44b053462744d162d77e4b341257c8eded586c1a Mon Sep 17 00:00:00 2001 From: Bill McCloskey Date: Wed, 10 May 2017 12:32:01 -0700 Subject: [PATCH 20/37] Bug 1363877 - Label PredictorLearnRunnable with SystemGroup (r=hurley) MozReview-Commit-ID: XPGp0226kb --- netwerk/base/Predictor.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/netwerk/base/Predictor.cpp b/netwerk/base/Predictor.cpp index 166d37dcb163b..80e20cc268deb 100644 --- a/netwerk/base/Predictor.cpp +++ b/netwerk/base/Predictor.cpp @@ -1550,7 +1550,9 @@ Predictor::LearnNative(nsIURI *targetURI, nsIURI *sourceURI, RefPtr runnable = new PredictorLearnRunnable( targetURI, sourceURI, reason, originAttributes); - NS_DispatchToMainThread(runnable); + SystemGroup::Dispatch("PredictorLearnRunnable", + TaskCategory::Other, + runnable.forget()); return NS_OK; } From 068699332fb352745f16785cfea18885f1edce77 Mon Sep 17 00:00:00 2001 From: Bill McCloskey Date: Wed, 10 May 2017 12:48:45 -0700 Subject: [PATCH 21/37] Bug 1363877 - Label nsBrowserStatusFilter::TimeoutHandler as SystemGroup (r=felipe) MozReview-Commit-ID: 5qD7vnU5GZC --- toolkit/components/statusfilter/nsBrowserStatusFilter.cpp | 8 +++++--- toolkit/content/browser-child.js | 6 ++++++ 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/toolkit/components/statusfilter/nsBrowserStatusFilter.cpp b/toolkit/components/statusfilter/nsBrowserStatusFilter.cpp index 4bb4f3e8c9e11..6358c96b2f034 100644 --- a/toolkit/components/statusfilter/nsBrowserStatusFilter.cpp +++ b/toolkit/components/statusfilter/nsBrowserStatusFilter.cpp @@ -4,14 +4,13 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "nsBrowserStatusFilter.h" +#include "mozilla/SystemGroup.h" #include "nsIChannel.h" #include "nsITimer.h" #include "nsIServiceManager.h" #include "nsString.h" -// XXX -// XXX DO NOT TOUCH THIS CODE UNLESS YOU KNOW WHAT YOU'RE DOING !!! -// XXX +using namespace mozilla; //----------------------------------------------------------------------------- // nsBrowserStatusFilter @@ -355,6 +354,9 @@ nsBrowserStatusFilter::StartDelayTimer() if (!mTimer) return NS_ERROR_FAILURE; + // Use the system group. The browser status filter is always used by chrome + // code. + mTimer->SetTarget(SystemGroup::EventTargetFor(TaskCategory::Other)); return mTimer->InitWithNamedFuncCallback( TimeoutHandler, this, 160, nsITimer::TYPE_ONE_SHOT, "nsBrowserStatusFilter::TimeoutHandler"); diff --git a/toolkit/content/browser-child.js b/toolkit/content/browser-child.js index 6ea92996702d2..a68a10f92d601 100644 --- a/toolkit/content/browser-child.js +++ b/toolkit/content/browser-child.js @@ -136,6 +136,9 @@ var WebProgressListener = { this._send("Content:StateChange", json, objects); }, + // Note: Because the nsBrowserStatusFilter timeout runnable is + // SystemGroup-labeled, this method should not modify content DOM or + // run content JS. onProgressChange: function onProgressChange(aWebProgress, aRequest, aCurSelf, aMaxSelf, aCurTotal, aMaxTotal) { let json = this._setupJSON(aWebProgress, aRequest); let objects = this._setupObjects(aWebProgress, aRequest); @@ -186,6 +189,9 @@ var WebProgressListener = { this._send("Content:LocationChange", json, objects); }, + // Note: Because the nsBrowserStatusFilter timeout runnable is + // SystemGroup-labeled, this method should not modify content DOM or + // run content JS. onStatusChange: function onStatusChange(aWebProgress, aRequest, aStatus, aMessage) { let json = this._setupJSON(aWebProgress, aRequest); let objects = this._setupObjects(aWebProgress, aRequest); From 2f2e996bf5dfa3754726a021748efefc81daa328 Mon Sep 17 00:00:00 2001 From: Bill McCloskey Date: Wed, 10 May 2017 12:52:20 -0700 Subject: [PATCH 22/37] Bug 1363877 - Label IPC shmem messages as SystemGroup (r=dvander) MozReview-Commit-ID: 3I5ny0wxVHI --- ipc/glue/ProtocolUtils.cpp | 10 ++++++++++ ipc/glue/ProtocolUtils.h | 4 ++++ xpcom/threads/SystemGroup.cpp | 8 ++++++++ xpcom/threads/SystemGroup.h | 3 +++ 4 files changed, 25 insertions(+) diff --git a/ipc/glue/ProtocolUtils.cpp b/ipc/glue/ProtocolUtils.cpp index 1d6559bf9201e..aec0ddeb2749d 100644 --- a/ipc/glue/ProtocolUtils.cpp +++ b/ipc/glue/ProtocolUtils.cpp @@ -19,6 +19,7 @@ #include "mozilla/ipc/MessageChannel.h" #include "mozilla/ipc/Transport.h" #include "mozilla/StaticMutex.h" +#include "mozilla/SystemGroup.h" #include "mozilla/Unused.h" #include "nsPrintfCString.h" @@ -632,6 +633,7 @@ IToplevelProtocol::Open(mozilla::ipc::Transport* aTransport, MessageLoop* aThread, mozilla::ipc::Side aSide) { + mIsMainThreadProtocol = NS_IsMainThread(); SetOtherProcessId(aOtherPid); return GetIPCChannel()->Open(aTransport, aThread, aSide); } @@ -641,6 +643,7 @@ IToplevelProtocol::Open(MessageChannel* aChannel, MessageLoop* aMessageLoop, mozilla::ipc::Side aSide) { + mIsMainThreadProtocol = NS_IsMainThread(); SetOtherProcessId(base::GetCurrentProcId()); return GetIPCChannel()->Open(aChannel, aMessageLoop, aSide); } @@ -815,6 +818,13 @@ IToplevelProtocol::ShmemDestroyed(const Message& aMsg) already_AddRefed IToplevelProtocol::GetMessageEventTarget(const Message& aMsg) { + if (IsMainThreadProtocol() && SystemGroup::Initialized()) { + if (aMsg.type() == SHMEM_CREATED_MESSAGE_TYPE || + aMsg.type() == SHMEM_DESTROYED_MESSAGE_TYPE) { + return do_AddRef(SystemGroup::EventTargetFor(TaskCategory::Other)); + } + } + int32_t route = aMsg.routing_id(); Maybe lock; diff --git a/ipc/glue/ProtocolUtils.h b/ipc/glue/ProtocolUtils.h index 8f02d85c81791..69ea6763f8321 100644 --- a/ipc/glue/ProtocolUtils.h +++ b/ipc/glue/ProtocolUtils.h @@ -386,6 +386,9 @@ class IToplevelProtocol : public IProtocol GetActorEventTarget(); virtual void OnChannelReceivedMessage(const Message& aMsg) {} + + bool IsMainThreadProtocol() const { return mIsMainThreadProtocol; } + protected: // Override this method in top-level protocols to change the event target // for a new actor (and its sub-actors). @@ -413,6 +416,7 @@ class IToplevelProtocol : public IProtocol int32_t mLastRouteId; IDMap mShmemMap; Shmem::id_t mLastShmemId; + bool mIsMainThreadProtocol; Mutex mEventTargetMutex; IDMap> mEventTargetMap; diff --git a/xpcom/threads/SystemGroup.cpp b/xpcom/threads/SystemGroup.cpp index a8ba2828a4484..5973907ed8921 100644 --- a/xpcom/threads/SystemGroup.cpp +++ b/xpcom/threads/SystemGroup.cpp @@ -22,6 +22,8 @@ class SystemGroupImpl final : public SchedulerGroup static void ShutdownStatic(); static SystemGroupImpl* Get(); + static bool Initialized() { return !!sSingleton; } + NS_METHOD_(MozExternalRefCountType) AddRef(void) { return 2; @@ -76,6 +78,12 @@ SystemGroup::Shutdown() SystemGroupImpl::ShutdownStatic(); } +bool +SystemGroup::Initialized() +{ + return SystemGroupImpl::Initialized(); +} + /* static */ nsresult SystemGroup::Dispatch(const char* aName, TaskCategory aCategory, diff --git a/xpcom/threads/SystemGroup.h b/xpcom/threads/SystemGroup.h index a8b70383164cc..1be37291f56b3 100644 --- a/xpcom/threads/SystemGroup.h +++ b/xpcom/threads/SystemGroup.h @@ -34,6 +34,9 @@ class SystemGroup static void InitStatic(); static void Shutdown(); + + // Returns true if SystemGroup has been initialized. + static bool Initialized(); }; } // namespace mozilla From e5fadfe35ff07fe74672d5af694f6b1845f17ba2 Mon Sep 17 00:00:00 2001 From: Bill McCloskey Date: Wed, 10 May 2017 13:52:21 -0700 Subject: [PATCH 23/37] Bug 1363877 - Label telemetry IPC with SystemGroup (r=chutten) MozReview-Commit-ID: GstPQfqdLFd --- .../telemetry/ipc/TelemetryIPCAccumulator.cpp | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/toolkit/components/telemetry/ipc/TelemetryIPCAccumulator.cpp b/toolkit/components/telemetry/ipc/TelemetryIPCAccumulator.cpp index 5b075c6f6f1a8..e9ed0ddffaa30 100644 --- a/toolkit/components/telemetry/ipc/TelemetryIPCAccumulator.cpp +++ b/toolkit/components/telemetry/ipc/TelemetryIPCAccumulator.cpp @@ -11,6 +11,7 @@ #include "mozilla/gfx/GPUProcessManager.h" #include "mozilla/StaticMutex.h" #include "mozilla/StaticPtr.h" +#include "mozilla/SystemGroup.h" #include "mozilla/Unused.h" #include "nsComponentManagerUtils.h" #include "nsITimer.h" @@ -21,6 +22,8 @@ using mozilla::StaticMutex; using mozilla::StaticMutexAutoLock; using mozilla::StaticAutoPtr; +using mozilla::SystemGroup; +using mozilla::TaskCategory; using mozilla::Telemetry::Accumulation; using mozilla::Telemetry::KeyedAccumulation; using mozilla::Telemetry::ScalarActionType; @@ -80,6 +83,7 @@ DoArmIPCTimerMainThread(const StaticMutexAutoLock& lock) CallCreateInstance(NS_TIMER_CONTRACTID, &gIPCTimer); } if (gIPCTimer) { + gIPCTimer->SetTarget(SystemGroup::EventTargetFor(TaskCategory::Other)); gIPCTimer->InitWithNamedFuncCallback(TelemetryIPCAccumulator::IPCTimerFired, nullptr, kBatchTimeoutMs, nsITimer::TYPE_ONE_SHOT, @@ -321,12 +325,6 @@ TelemetryIPCAccumulator::DeInitializeGlobalState() void TelemetryIPCAccumulator::DispatchToMainThread(already_AddRefed&& aEvent) { - nsCOMPtr event(aEvent); - nsCOMPtr thread; - nsresult rv = NS_GetMainThread(getter_AddRefs(thread)); - if (NS_FAILED(rv)) { - NS_WARNING("NS_FAILED DispatchToMainThread. Maybe we're shutting down?"); - return; - } - thread->Dispatch(event, 0); + SystemGroup::EventTargetFor(TaskCategory::Other)->Dispatch(Move(aEvent), + nsIEventTarget::DISPATCH_NORMAL); } From 68a155951ce444296f51b2e17fd83b19999b418c Mon Sep 17 00:00:00 2001 From: Bill McCloskey Date: Tue, 9 May 2017 13:41:11 -0700 Subject: [PATCH 24/37] Bug 1351021 - Make SchedulerGroup::Runnable QIable (r=froydnj) The patch allows us to tell whether a runnable is labeled using QI. MozReview-Commit-ID: KkcCSUdWMBr --- xpcom/threads/SchedulerGroup.cpp | 59 ++++++++++++++------------------ xpcom/threads/SchedulerGroup.h | 37 +++++++++++++++++--- 2 files changed, 59 insertions(+), 37 deletions(-) diff --git a/xpcom/threads/SchedulerGroup.cpp b/xpcom/threads/SchedulerGroup.cpp index 856fa8f9ceb64..efade2cb1a981 100644 --- a/xpcom/threads/SchedulerGroup.cpp +++ b/xpcom/threads/SchedulerGroup.cpp @@ -19,35 +19,6 @@ using namespace mozilla; -class SchedulerGroup::Runnable final : public mozilla::Runnable -{ -public: - Runnable(already_AddRefed&& aRunnable, - SchedulerGroup* aDispatcher); - - NS_IMETHODIMP - GetName(nsACString& aName) override - { - mozilla::Runnable::GetName(aName); - if (aName.IsEmpty()) { - // Try to get a name from the underlying runnable. - nsCOMPtr named = do_QueryInterface(mRunnable); - if (named) { - named->GetName(aName); - } - } - aName.AppendASCII("(labeled)"); - return NS_OK; - } - - bool IsBackground() const { return mDispatcher->IsBackground(); } - - NS_DECL_NSIRUNNABLE -private: - nsCOMPtr mRunnable; - RefPtr mDispatcher; -}; - /* SchedulerEventTarget */ namespace { @@ -350,18 +321,36 @@ SchedulerGroup::SetValidatingAccess(ValidationType aType) } SchedulerGroup::Runnable::Runnable(already_AddRefed&& aRunnable, - SchedulerGroup* aDispatcher) + SchedulerGroup* aGroup) : mRunnable(Move(aRunnable)), - mDispatcher(aDispatcher) + mGroup(aGroup) { } +NS_IMETHODIMP +SchedulerGroup::Runnable::GetName(nsACString& aName) +{ + mozilla::Runnable::GetName(aName); + if (aName.IsEmpty()) { + // Try to get a name from the underlying runnable. + nsCOMPtr named = do_QueryInterface(mRunnable); + if (named) { + named->GetName(aName); + } + if (aName.IsEmpty()) { + aName.AssignLiteral("anonymous"); + } + } + aName.AppendASCII("(labeled)"); + return NS_OK; +} + NS_IMETHODIMP SchedulerGroup::Runnable::Run() { MOZ_RELEASE_ASSERT(NS_IsMainThread()); - mDispatcher->SetValidatingAccess(StartValidation); + mGroup->SetValidatingAccess(StartValidation); nsresult result; @@ -374,10 +363,14 @@ SchedulerGroup::Runnable::Run() // the scope of the TabGroup. mRunnable = nullptr; - mDispatcher->SetValidatingAccess(EndValidation); + mGroup->SetValidatingAccess(EndValidation); return result; } +NS_IMPL_ISUPPORTS_INHERITED(SchedulerGroup::Runnable, + mozilla::Runnable, + SchedulerGroup::Runnable) + SchedulerGroup::AutoProcessEvent::AutoProcessEvent() : mPrevRunningDispatcher(SchedulerGroup::sRunningDispatcher) { diff --git a/xpcom/threads/SchedulerGroup.h b/xpcom/threads/SchedulerGroup.h index 841956babf962..3bce3fb323d1a 100644 --- a/xpcom/threads/SchedulerGroup.h +++ b/xpcom/threads/SchedulerGroup.h @@ -12,6 +12,7 @@ #include "mozilla/TimeStamp.h" #include "nsCOMPtr.h" #include "nsISupportsImpl.h" +#include "nsThreadUtils.h" class nsIEventTarget; class nsIRunnable; @@ -22,16 +23,20 @@ namespace dom { class TabGroup; } +#define NS_SCHEDULERGROUPRUNNABLE_IID \ +{ 0xd31b7420, 0x872b, 0x4cfb, \ + { 0xa9, 0xc6, 0xae, 0x4c, 0x0f, 0x06, 0x36, 0x74 } } + // The "main thread" in Gecko will soon be a set of cooperatively scheduled // "fibers". Global state in Gecko will be partitioned into a series of "groups" // (with roughly one group per tab). Runnables will be annotated with the set of // groups that they touch. Two runnables may run concurrently on different // fibers as long as they touch different groups. // -// A Dispatcher is an abstract class to represent a "group". Essentially the -// only functionality offered by a Dispatcher is the ability to dispatch +// A SchedulerGroup is an abstract class to represent a "group". Essentially the +// only functionality offered by a SchedulerGroup is the ability to dispatch // runnables to the group. TabGroup, DocGroup, and SystemGroup are the concrete -// implementations of Dispatcher. +// implementations of SchedulerGroup. class SchedulerGroup { public: @@ -58,7 +63,29 @@ class SchedulerGroup MOZ_ASSERT(!sRunningDispatcher || mAccessValid); } - class Runnable; + class Runnable final : public mozilla::Runnable + { + public: + Runnable(already_AddRefed&& aRunnable, + SchedulerGroup* aGroup); + + SchedulerGroup* Group() const { return mGroup; } + + NS_IMETHOD GetName(nsACString& aName) override; + + bool IsBackground() const { return mGroup->IsBackground(); } + + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_NSIRUNNABLE + + NS_DECLARE_STATIC_IID_ACCESSOR(NS_SCHEDULERGROUPRUNNABLE_IID); + + private: + ~Runnable() = default; + + nsCOMPtr mRunnable; + RefPtr mGroup; + }; friend class Runnable; bool* GetValidAccessPtr() { return &mAccessValid; } @@ -121,6 +148,8 @@ class SchedulerGroup RefPtr mAbstractThreads[size_t(TaskCategory::Count)]; }; +NS_DEFINE_STATIC_IID_ACCESSOR(SchedulerGroup::Runnable, NS_SCHEDULERGROUPRUNNABLE_IID); + } // namespace mozilla #endif // mozilla_SchedulerGroup_h From 71bfb5a04b9bfa36523806a14de3136f93232cc1 Mon Sep 17 00:00:00 2001 From: Bill McCloskey Date: Tue, 9 May 2017 16:16:51 -0700 Subject: [PATCH 25/37] Bug 1351021 - Compute mean time between unlabeled (r=froydnj,data-r=bsmedberg) Accumulates the time in between running unlabeled runnables in a histogram. This measurement will be useful to see how much of a win the cooperative scheduler will be, assuming we label no more runnables. MozReview-Commit-ID: 9lgoGJcXLP9 --- toolkit/components/telemetry/Histograms.json | 10 ++++ xpcom/threads/nsThread.cpp | 53 +++++++++++++++----- xpcom/threads/nsThread.h | 7 +++ 3 files changed, 58 insertions(+), 12 deletions(-) diff --git a/toolkit/components/telemetry/Histograms.json b/toolkit/components/telemetry/Histograms.json index 599e0372c2d6e..f40419200221e 100644 --- a/toolkit/components/telemetry/Histograms.json +++ b/toolkit/components/telemetry/Histograms.json @@ -13072,5 +13072,15 @@ "n_buckets": 100, "bug_numbers": [1345540], "description": "Time (ms) for the APZ handled wheel event spent in handlers." + }, + "TIME_BETWEEN_UNLABELED_RUNNABLES_MS": { + "record_in_processes": ["content"], + "alert_emails": ["wmccloskey@mozilla.com"], + "expires_in_version": "60", + "kind": "exponential", + "high": 30000, + "n_buckets": 30, + "bug_numbers": [1351021], + "description": "Every time we run an unlabeled runnable, this histogram records the time (in ms) since the last unlabeled runnable ran." } } diff --git a/xpcom/threads/nsThread.cpp b/xpcom/threads/nsThread.cpp index 6fb66e794991d..ee48fd82e3628 100644 --- a/xpcom/threads/nsThread.cpp +++ b/xpcom/threads/nsThread.cpp @@ -481,7 +481,7 @@ nsThread::ThreadFunc(void* aArg) nsCOMPtr event; { MutexAutoLock lock(self->mLock); - if (!self->mEvents->GetEvent(true, getter_AddRefs(event), lock)) { + if (!self->mEvents->GetEvent(true, getter_AddRefs(event), nullptr, lock)) { NS_WARNING("failed waiting for thread startup event"); return; } @@ -642,6 +642,7 @@ nsThread::nsThread(MainThreadFlag aMainThread, uint32_t aStackSize) , mShutdownRequired(false) , mEventsAreDoomed(false) , mIsMainThread(aMainThread) + , mLastUnlabeledRunnable(TimeStamp::Now()) , mCanInvokeJS(false) { } @@ -809,6 +810,7 @@ nsThread::DispatchInternal(already_AddRefed aEvent, uint32_t aFlags bool nsThread::nsChainedEventQueue::GetEvent(bool aMayWait, nsIRunnable** aEvent, + unsigned short* aPriority, mozilla::MutexAutoLock& aProofOfLock) { bool retVal = false; @@ -817,6 +819,9 @@ nsThread::nsChainedEventQueue::GetEvent(bool aMayWait, nsIRunnable** aEvent, MOZ_ASSERT(mSecondaryQueue->HasPendingEvent(aProofOfLock)); retVal = mSecondaryQueue->GetEvent(aMayWait, aEvent, aProofOfLock); MOZ_ASSERT(*aEvent); + if (aPriority) { + *aPriority = nsIRunnablePriority::PRIORITY_HIGH; + } mProcessSecondaryQueueRunnable = false; return retVal; } @@ -826,6 +831,9 @@ nsThread::nsChainedEventQueue::GetEvent(bool aMayWait, nsIRunnable** aEvent, aMayWait && !mSecondaryQueue->HasPendingEvent(aProofOfLock); retVal = mNormalQueue->GetEvent(reallyMayWait, aEvent, aProofOfLock); + if (aPriority) { + *aPriority = nsIRunnablePriority::PRIORITY_NORMAL; + } // Let's see if we should next time process an event from the secondary // queue. @@ -1157,14 +1165,16 @@ nsThread::GetIdleEvent(nsIRunnable** aEvent, MutexAutoLock& aProofOfLock) } void -nsThread::GetEvent(bool aWait, nsIRunnable** aEvent, MutexAutoLock& aProofOfLock) +nsThread::GetEvent(bool aWait, nsIRunnable** aEvent, + unsigned short* aPriority, + MutexAutoLock& aProofOfLock) { MOZ_ASSERT(PR_GetCurrentThread() == mThread); MOZ_ASSERT(aEvent); // We'll try to get an event to execute in three stages. // [1] First we just try to get it from the regular queue without waiting. - mEvents->GetEvent(false, aEvent, aProofOfLock); + mEvents->GetEvent(false, aEvent, aPriority, aProofOfLock); // [2] If we didn't get an event from the regular queue, try to // get one from the idle queue @@ -1175,6 +1185,11 @@ nsThread::GetEvent(bool aWait, nsIRunnable** aEvent, MutexAutoLock& aProofOfLock // wait for an idle event, since a higher priority event might // appear at any time. GetIdleEvent(aEvent, aProofOfLock); + + if (*aEvent && aPriority) { + // Idle events count as normal priority. + *aPriority = nsIRunnablePriority::PRIORITY_NORMAL; + } } // [3] If we neither got an event from the regular queue nor the @@ -1182,7 +1197,7 @@ nsThread::GetEvent(bool aWait, nsIRunnable** aEvent, MutexAutoLock& aProofOfLock // main queue until an event is available. // If we are shutting down, then do not wait for new events. if (!*aEvent && aWait) { - mEvents->GetEvent(aWait, aEvent, aProofOfLock); + mEvents->GetEvent(aWait, aEvent, aPriority, aProofOfLock); } } @@ -1241,9 +1256,10 @@ nsThread::ProcessNextEvent(bool aMayWait, bool* aResult) // mNestedEventLoopDepth has been incremented, since that destructor can // also do work. nsCOMPtr event; + unsigned short priority; { MutexAutoLock lock(mLock); - GetEvent(reallyWait, getter_AddRefs(event), lock); + GetEvent(reallyWait, getter_AddRefs(event), &priority, lock); } *aResult = (event.get() != nullptr); @@ -1258,15 +1274,28 @@ nsThread::ProcessNextEvent(bool aMayWait, bool* aResult) #ifndef RELEASE_OR_BETA nsCString name; - if (nsCOMPtr named = do_QueryInterface(event)) { - if (NS_FAILED(named->GetName(name))) { - name.AssignLiteral("GetName failed"); - } else if (name.IsEmpty()) { - name.AssignLiteral("anonymous runnable"); - } + bool labeled = false; + if (RefPtr groupRunnable = do_QueryObject(event)) { + labeled = true; + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(groupRunnable->GetName(name))); + } else if (nsCOMPtr named = do_QueryInterface(event)) { + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(named->GetName(name))); } else { name.AssignLiteral("non-nsINamed runnable"); } + if (name.IsEmpty()) { + name.AssignLiteral("anonymous runnable"); + } + + // High-priority runnables are ignored here since they'll run right away + // even with the cooperative scheduler. + if (!labeled && priority == nsIRunnablePriority::PRIORITY_NORMAL) { + TimeStamp now = TimeStamp::Now(); + double diff = (now - mLastUnlabeledRunnable).ToMilliseconds(); + Telemetry::Accumulate(Telemetry::TIME_BETWEEN_UNLABELED_RUNNABLES_MS, diff); + mLastUnlabeledRunnable = now; + } + timer.emplace(name); #endif } @@ -1461,7 +1490,7 @@ nsThread::PopEventQueue(nsIEventTarget* aInnermostTarget) mEvents = WrapNotNull(mEvents->mNext); nsCOMPtr event; - while (queue->GetEvent(false, getter_AddRefs(event), lock)) { + while (queue->GetEvent(false, getter_AddRefs(event), nullptr, lock)) { mEvents->PutEvent(event.forget(), lock); } diff --git a/xpcom/threads/nsThread.h b/xpcom/threads/nsThread.h index fd8209b0b5c48..4705bf9290c61 100644 --- a/xpcom/threads/nsThread.h +++ b/xpcom/threads/nsThread.h @@ -17,6 +17,7 @@ #include "nsTObserverArray.h" #include "mozilla/Attributes.h" #include "mozilla/NotNull.h" +#include "mozilla/TimeStamp.h" #include "nsAutoPtr.h" #include "mozilla/AlreadyAddRefed.h" #include "mozilla/UniquePtr.h" @@ -98,6 +99,7 @@ class nsThread void GetIdleEvent(nsIRunnable** aEvent, mozilla::MutexAutoLock& aProofOfLock); void GetEvent(bool aWait, nsIRunnable** aEvent, + unsigned short* aPriority, mozilla::MutexAutoLock& aProofOfLock); protected: @@ -154,6 +156,7 @@ class nsThread } bool GetEvent(bool aMayWait, nsIRunnable** aEvent, + unsigned short* aPriority, mozilla::MutexAutoLock& aProofOfLock); void PutEvent(nsIRunnable* aEvent, mozilla::MutexAutoLock& aProofOfLock) @@ -270,6 +273,10 @@ class nsThread bool mEventsAreDoomed; MainThreadFlag mIsMainThread; + // The time when we last ran an unlabeled runnable (one not associated with a + // SchedulerGroup). + mozilla::TimeStamp mLastUnlabeledRunnable; + // Set to true if this thread creates a JSRuntime. bool mCanInvokeJS; }; From 4d16361faf49d4dfe750757a44f59f55a5170534 Mon Sep 17 00:00:00 2001 From: Nathan Froyd Date: Wed, 17 May 2017 14:50:08 -0400 Subject: [PATCH 26/37] Bug 1365668 - turn off select warnings for ICU; r=ted.mielczarek We don't really care about these warnings, and they're just cluttering the build. --- config/external/icu/common/moz.build | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/config/external/icu/common/moz.build b/config/external/icu/common/moz.build index 031f7f8136a2b..637c9f5a3dff8 100644 --- a/config/external/icu/common/moz.build +++ b/config/external/icu/common/moz.build @@ -14,5 +14,15 @@ DEFINES['LOCALE_SNAME'] = 0x5c LOCAL_INCLUDES += ['/intl/icu/source/i18n'] +if CONFIG['GNU_CXX']: + CXXFLAGS += [ + '-Wno-deprecated-declarations', + '-Wno-type-limits', + '-Wno-unused-but-set-variable', + '-Wno-unused-function', + '-Wno-sign-compare', + '-Wno-maybe-uninitialized', + ] + include('../defs.mozbuild') include('sources.mozbuild') From 03467c51b9f58e8c5b72a5f0a4a7be27d50fc886 Mon Sep 17 00:00:00 2001 From: Jason Laster Date: Wed, 17 May 2017 20:50:22 +0200 Subject: [PATCH 27/37] Bug 1365059 - Update Debugger frontend (5/15/2017). r=jdescottes MozReview-Commit-ID: 34vyUjfmd1Z --HG-- extra : rebase_source : 52d4c3032cbc490b5d0556c2744a35623211c4ea --- devtools/client/debugger/new/debugger.css | 224 +- devtools/client/debugger/new/debugger.js | 2369 ++++++++++++----- devtools/client/debugger/new/parser-worker.js | 327 ++- .../test/mochitest/browser_dbg-expressions.js | 2 +- .../mochitest/browser_dbg-scopes-mutations.js | 37 +- .../new/test/mochitest/browser_dbg-scopes.js | 6 +- .../new/test/mochitest/browser_dbg-sources.js | 38 +- .../debugger/new/test/mochitest/head.js | 27 +- .../framework/toolbox-process-window.js | 2 +- .../client/locales/en-US/debugger.properties | 26 +- 10 files changed, 2142 insertions(+), 916 deletions(-) diff --git a/devtools/client/debugger/new/debugger.css b/devtools/client/debugger/new/debugger.css index ea4b3654964c7..acac4e9c4c889 100644 --- a/devtools/client/debugger/new/debugger.css +++ b/devtools/client/debugger/new/debugger.css @@ -979,14 +979,13 @@ html .arrow.expanded svg { .search-field .search-nav-buttons .nav-btn path { fill: var(--theme-comment); } - .result-list { list-style: none; - width: 100%; background-color: var(--theme-toolbar-background); margin: 0px; padding: 0px; overflow: auto; + width: calc(100% - 1px); /* 1px fixes the hidden right border */ } .result-list.big { @@ -1004,10 +1003,7 @@ html .arrow.expanded svg { padding: 4px 13px; display: flex; justify-content: space-between; -} - -.result-list li:first-child { - border-top: none; + border: 1px solid transparent; } .result-list.big li { @@ -1022,12 +1018,7 @@ html .arrow.expanded svg { } .result-list li.selected { - border: 1px solid var(--theme-selection-background); -} - -.result-list.big li.selected { - padding-left: 9px; - padding-top: 9px; + border-color: var(--theme-selection-background); } .search-bar .result-list li.selected { @@ -1035,6 +1026,10 @@ html .arrow.expanded svg { color: white; } +.theme-dark .search-bar .result-list li.selected { + background-color: var(--theme-body-background); +} + .result-list li .title { line-height: 1.5em; word-break: break-all; @@ -1132,6 +1127,35 @@ html .arrow.expanded svg { padding-top: 5px; text-align: center; } +.outline-list { + list-style-type: "-"; +} + +.outline-list__element { + color: blue; + padding-left: 0.5rem; +} + +.function-signature { + line-height: 20px; + align-self: center; +} + +.function-signature .function-name { + color: var(--theme-highlight-blue); +} + +.function-signature .param { + color: var(--string-color); +} + +.function-signature .paren { + color: var(--object-color); +} + +.function-signature .comma { + color: var(--object-color); +} .tree { -webkit-user-select: none; -moz-user-select: none; @@ -1159,6 +1183,10 @@ html .arrow.expanded svg { background-color: var(--theme-selection-background); } +.theme-dark .tree .node.focused { + background-color: var(--theme-selection-background-semitransparent); +} + html:not([dir="rtl"]) .tree .node > div { margin-left: 10px; } @@ -1209,6 +1237,11 @@ html[dir="rtl"] .tree .node > div { transform: translate(0, 2px); transition: transform 0.25s ease-in-out; cursor: pointer; + padding: 5px 2px; +} + +.toggle-button-start.vertical, +.toggle-button-end.vertical { padding: 4px 2px; } @@ -1241,10 +1274,6 @@ html .toggle-button-end.vertical svg { transform: rotate(-90deg); } -.toggle-button-end.vertical { - margin-bottom: 2px; -} - .toggle-button-start.collapsed, .toggle-button-end.collapsed { transform: rotate(180deg); @@ -1454,78 +1483,59 @@ html .toggle-button-end.vertical svg { .search-bar .result-list { max-height: 230px; } - -.function-signature { - line-height: 20px; - align-self: center; -} - -.function-signature .function-name { - color: var(--theme-highlight-blue); -} - -.function-signature .param { - color: var(--string-color); -} - -.function-signature .paren { - color: var(--object-color); -} - -.function-signature .comma { - color: var(--object-color); -} -.popover { - position: fixed; - z-index: 100; - box-shadow: 1px 2px 4px 1px var(--theme-toolbar-background-alt); +.object-value .unavailable { + color: var(--theme-comment); } - -.popover .gap { - height: 5px; - padding-top: 5px; +.bracket-arrow { + position: absolute; } -.popover::before, -.popover::after { +.bracket-arrow::before, +.bracket-arrow::after { content: ''; height: 0; width: 0; position: absolute; border: 7px solid transparent; - left: calc(20% - 7px); /* corresponds to calculation in Popover.js */ } -.popover::before { +.bracket-arrow.up::before { border-bottom-color: var(--theme-splitter-color); - top: -9px; + top: -1px; } -.theme-dark .popover::before { +.theme-dark .bracket-arrow.up::before { border-bottom-color: var(--theme-body-color); } -.popover::after { +.bracket-arrow.up::after { border-bottom-color: var(--theme-body-background); - top: -8px; + top: 0px; } -.popover.up::before { +.bracket-arrow.down::before { border-bottom-color: transparent; border-top-color: var(--theme-splitter-color); - bottom: -9px; - top: initial; + top: 0px; } -.theme-dark .popover.up::before { +.theme-dark .bracket-arrow.down::before { border-top-color: var(--theme-body-color); } -.popover.up::after { +.bracket-arrow.down::after { border-bottom-color: transparent; - border-top-color: var(--theme-body-background); - bottom: -8px; - top: initial; + border-top-color: var(--theme-toolbar-background); + top: -1px; +} +.popover { + position: fixed; + z-index: 100; +} + +.popover .gap { + height: 5px; + padding-top: 5px; } .popover .preview { background: var(--theme-body-background); @@ -1607,6 +1617,7 @@ html .toggle-button-end.vertical svg { .add-to-expression-bar { border: 1px solid var(--theme-splitter-color); + border-top: none; display: -webkit-box; display: -ms-flexbox; display: flex; @@ -1633,11 +1644,15 @@ html .toggle-button-end.vertical svg { color: var(--theme-comment); cursor: pointer; } -/* vim:set ts=2 sw=2 sts=2 et: */ +.editor-wrapper { + --debug-line-background: rgba(226, 236, 247, 0.5); + --debug-line-border: rgb(145, 188, 219); +} -/* 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/. */ +.theme-dark .editor-wrapper { + --debug-line-background: rgb(73, 82, 103); + --debug-line-border: rgb(119, 134, 162); +} /** * There's a known codemirror flex issue with chrome that this addresses. @@ -1677,6 +1692,10 @@ html[dir="rtl"] .editor-mount { height: 14px; } +.editor-wrapper .highlight-lines { + background: var(--theme-selection-background-semitransparent); +} + .coverage-on .CodeMirror-code :not(.hit-marker) .CodeMirror-line, .coverage-on .CodeMirror-code :not(.hit-marker) .CodeMirror-gutter-wrapper { opacity: 0.5; @@ -1717,6 +1736,17 @@ html[dir="rtl"] .editor-mount { opacity: 0.3; } +.editor.column-breakpoint svg { + fill: var(--theme-selection-background); + vertical-align: middle; + width: 17px; + height: 14px; +} + +.editor.column-breakpoint.breakpoint-disabled svg { + opacity: 0.3; +} + .CodeMirror { width: 100%; height: 100%; @@ -1756,13 +1786,18 @@ html[dir="rtl"] .editor-mount { color: var(--theme-content-color3); } -.debug-line .CodeMirror-line { - background-color: var(--breakpoint-active-color) !important; +.new-debug-line .CodeMirror-line { + background-color: var(--debug-line-background) !important; + outline: var(--debug-line-border) solid 1px; +} + +.new-debug-line .CodeMirror-linenumber { + background-color: transparent !important; } /* Don't display the highlight color since the debug line is already highlighted */ -.debug-line .CodeMirror-activeline-background { +.new-debug-line .CodeMirror-activeline-background { display: none; } @@ -1812,13 +1847,13 @@ html[dir="rtl"] .editor-mount { } .why-paused { - background-color: var(--breakpoint-active-color); + background-color: var(--theme-body-background); + border-bottom: 1px solid var(--theme-splitter-color); color: var(--theme-highlight-blue); padding: 10px 10px 10px 20px; white-space: normal; opacity: 0.9; font-size: 12px; - font-weight: bold; flex: 0 1 auto; } @@ -1829,6 +1864,10 @@ html[dir="rtl"] .editor-mount { .why-paused .message { font-size: 10px; } +.breakpoints-toggle { + margin: 2px 3px; +} + .breakpoints-list * { -moz-user-select: none; user-select: none; @@ -1884,6 +1923,7 @@ html .breakpoints-list .breakpoint.paused { .breakpoints-list .breakpoint-checkbox { margin-inline-start: 0; + vertical-align: -2px; } .breakpoints-list .breakpoint-label { @@ -1916,8 +1956,9 @@ html .breakpoints-list .breakpoint.paused { .breakpoint .close-btn { position: absolute; - offset-inline-end: 16px; - top: 12px; + offset-inline-end: 13px; + offset-inline-start: auto; + top: 9px; } .breakpoint .close { @@ -2410,10 +2451,16 @@ html .command-bar > button:disabled { z-index: 100; } -html .welcomebox .toggle-button-end { - bottom: 11px; +.welcomebox .toggle-button-end { position: absolute; top: auto; + bottom: 0; + offset-inline-end: 0; + offset-inline-start: auto; +} + +html .welcomebox .toggle-button-end.collapsed { + bottom: 1px; } .source-header { border-bottom: 1px solid var(--theme-splitter-color); @@ -2430,7 +2477,7 @@ html .welcomebox .toggle-button-end { .source-header .new-tab-btn { padding: 0px 4px; - margin-top: 8px; + margin-top: 4px; cursor: pointer; fill: var(--theme-comment); transition: 0.1s ease; @@ -2450,15 +2497,15 @@ html .welcomebox .toggle-button-end { border: 1px solid transparent; border-top-left-radius: 2px; border-top-right-radius: 2px; - height: 30px; display: inline-flex; - align-items: center; + align-items: flex-end; position: relative; transition: all 0.25s ease; min-width: 40px; overflow: hidden; - padding: 6px; + padding: 5px; margin-inline-start: 3px; + margin-top: 4px; } .source-tab:hover { @@ -2492,9 +2539,10 @@ html .welcomebox .toggle-button-end { fill: var(--theme-textbox-box-shadow); } -.source-tab .blackBox { +.source-tab .blackBox, +.source-tab .prettyPrint { line-height: 0; - padding: 5px; + align-self: center; } .source-tab .blackBox svg { @@ -2506,20 +2554,21 @@ html .welcomebox .toggle-button-end { fill: var(--theme-textbox-box-shadow); } +.theme-dark .source-tab .blackBox circle { + fill: var(--theme-body-color); +} + .source-tab .filename { white-space: nowrap; text-overflow: ellipsis; overflow: hidden; -} - -.source-tab.pretty .filename { - padding-inline-start: 8px; + padding: 0 4px; + align-self: flex-start; } .source-tab .close-btn { visibility: hidden; line-height: 0; - margin-inline-start: 6px; } .source-tab.active .close-btn { @@ -2536,8 +2585,8 @@ html .welcomebox .toggle-button-end { box-shadow: 0 4px 4px 0 var(--search-overlays-semitransparent); max-height: 300px; position: absolute; - right: 8px; - top: 35px; + right: 0; + top: 23px; width: var(--width); z-index: 1000; } @@ -2559,7 +2608,6 @@ html[dir="rtl"] .dropdown { border: none; padding: 0; font-weight: 100; - margin-top: 6px; font-size: 14px; } diff --git a/devtools/client/debugger/new/debugger.js b/devtools/client/debugger/new/debugger.js index 4e1a116f65fb1..4e04aba01e33e 100644 --- a/devtools/client/debugger/new/debugger.js +++ b/devtools/client/debugger/new/debugger.js @@ -7,7 +7,7 @@ var a = typeof exports === 'object' ? factory(require("devtools/client/shared/vendor/react"), require("Services"), require("devtools/shared/flags"), require("devtools/client/sourceeditor/editor")) : factory(root["devtools/client/shared/vendor/react"], root["Services"], root["devtools/shared/flags"], root["devtools/client/sourceeditor/editor"]); for(var i in a) (typeof exports === 'object' ? exports : root)[i] = a[i]; } -})(this, function(__WEBPACK_EXTERNAL_MODULE_2__, __WEBPACK_EXTERNAL_MODULE_29__, __WEBPACK_EXTERNAL_MODULE_121__, __WEBPACK_EXTERNAL_MODULE_994__) { +})(this, function(__WEBPACK_EXTERNAL_MODULE_2__, __WEBPACK_EXTERNAL_MODULE_29__, __WEBPACK_EXTERNAL_MODULE_121__, __WEBPACK_EXTERNAL_MODULE_995__) { return /******/ (function(modules) { // webpackBootstrap /******/ // The module cache /******/ var installedModules = {}; @@ -3384,7 +3384,7 @@ return /******/ (function(modules) { // webpackBootstrap showMenu = _require6.showMenu, buildMenu = _require6.buildMenu; - setConfig(({"environment":"firefox-panel","logging":false,"clientLogging":false,"firefox":{"mcPath":"./firefox"},"workers":{"parserURL":"resource://devtools/client/debugger/new/parser-worker.js","prettyPrintURL":"resource://devtools/client/debugger/new/pretty-print-worker.js"},"features":{"blackbox":{"enabled":true},"chromeScopes":{"enabled":false},"eventListeners":{"enabled":false},"codeCoverage":{"enabled":false}}})); + setConfig(({"environment":"firefox-panel","logging":false,"clientLogging":false,"firefox":{"mcPath":"./firefox"},"workers":{"parserURL":"resource://devtools/client/debugger/new/parser-worker.js","prettyPrintURL":"resource://devtools/client/debugger/new/pretty-print-worker.js"},"features":{"blackbox":{"enabled":true},"chromeScopes":{"enabled":false},"eventListeners":{"enabled":false},"codeCoverage":{"enabled":false},"searchNav":{"enabled":true}}})); // Set various flags before requiring app code. if (getValue("logging.client")) { @@ -12049,6 +12049,8 @@ return /******/ (function(modules) { // webpackBootstrap var _thunk = __webpack_require__(224); + var _timing = __webpack_require__(992); + /** * This creates a dispatcher with all the standard middleware in place * that all code requires. It can also be optionally configured in @@ -12068,18 +12070,6 @@ return /******/ (function(modules) { // webpackBootstrap * @memberof utils/create-store * @static */ - - - /* 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/. */ - /* global window */ - - /** - * Redux store utils - * @module utils/create-store - */ - var configureStore = function () { var opts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; @@ -12103,12 +12093,26 @@ return /******/ (function(modules) { // webpackBootstrap middleware.push(_log.log); } + if (opts.timing) { + middleware.push(_timing.timing); + } + // Hook in the redux devtools browser extension if it exists var devtoolsExt = typeof window === "object" && window.devToolsExtension ? window.devToolsExtension() : f => f; return _redux.applyMiddleware.apply(undefined, middleware)(devtoolsExt(_redux.createStore)); }; + /* 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/. */ + /* global window */ + + /** + * Redux store utils + * @module utils/create-store + */ + exports.default = configureStore; /***/ }, @@ -13399,7 +13403,7 @@ return /******/ (function(modules) { // webpackBootstrap var _immutable = __webpack_require__(146); - var _reselect = __webpack_require__(992); + var _reselect = __webpack_require__(993); var _prefs = __webpack_require__(226); @@ -13531,6 +13535,8 @@ return /******/ (function(modules) { // webpackBootstrap LOAD_SOURCE_TEXT: "LOAD_SOURCE_TEXT", SELECT_SOURCE: "SELECT_SOURCE", SELECT_SOURCE_URL: "SELECT_SOURCE_URL", + HIGHLIGHT_LINES: "HIGHLIGHT_LINES", + CLEAR_HIGHLIGHT_LINES: "CLEAR_HIGHLIGHT_LINES", CLOSE_TAB: "CLOSE_TAB", CLOSE_TABS: "CLOSE_TABS", NAVIGATE: "NAVIGATE", @@ -13569,21 +13575,15 @@ return /******/ (function(modules) { // webpackBootstrap "use strict"; - /** - * When Flow 0.29 is released (very soon), we can use this Record type - * instead of the builtin immutable.js Record type. This is better - * because all the fields are actually typed, unlike the builtin one. - * This depends on a performance fix that will go out in 0.29 though; - * @module utils/makeRecord - */ + Object.defineProperty(exports, "__esModule", { + value: true + }); - var I = __webpack_require__(146); + var _immutable = __webpack_require__(146); - /** - * @memberof utils/makeRecord - * @static - */ + var I = _interopRequireWildcard(_immutable); + function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } /** * Make an immutable record type @@ -13593,11 +13593,25 @@ return /******/ (function(modules) { // webpackBootstrap * @memberof utils/makeRecord * @static */ + + + /** + * @memberof utils/makeRecord + * @static + */ function makeRecord(spec) { return I.Record(spec); } - module.exports = makeRecord; + /** + * When Flow 0.29 is released (very soon), we can use this Record type + * instead of the builtin immutable.js Record type. This is better + * because all the fields are actually typed, unlike the builtin one. + * This depends on a performance fix that will go out in 0.29 though; + * @module utils/makeRecord + */ + + exports.default = makeRecord; /***/ }, /* 231 */ @@ -13676,7 +13690,7 @@ return /******/ (function(modules) { // webpackBootstrap var I = _interopRequireWildcard(_immutable); - var _reselect = __webpack_require__(992); + var _reselect = __webpack_require__(993); var _makeRecord = __webpack_require__(230); @@ -13876,7 +13890,7 @@ return /******/ (function(modules) { // webpackBootstrap return ""; } - var selectedTab = state.sources.find(source => source.get("id") == selectedLocation.sourceId); + var selectedTab = state.sources.get(selectedLocation.sourceId); var selectedTabUrl = selectedTab ? selectedTab.get("url") : ""; @@ -13974,7 +13988,7 @@ return /******/ (function(modules) { // webpackBootstrap return; } - return sources.find(source => source.get("id") == selectedLocation.sourceId); + return sources.get(selectedLocation.sourceId); }); exports.default = update; @@ -14248,6 +14262,9 @@ return /******/ (function(modules) { // webpackBootstrap "use strict"; + Object.defineProperty(exports, "__esModule", { + value: true + }); function basename(path) { return path.split("/").pop(); } @@ -14269,13 +14286,11 @@ return /******/ (function(modules) { // webpackBootstrap return `${base}/${dir}`; } - module.exports = { - basename, - dirname, - isURL, - isAbsolute, - join - }; + exports.basename = basename; + exports.dirname = dirname; + exports.isURL = isURL; + exports.isAbsolute = isAbsolute; + exports.join = join; /***/ }, /* 236 */ @@ -14288,6 +14303,8 @@ return /******/ (function(modules) { // webpackBootstrap }); exports.State = undefined; exports.makeLocationId = makeLocationId; + exports.makePendingLocationId = makePendingLocationId; + exports.makePendingBreakpoint = makePendingBreakpoint; exports.getBreakpoint = getBreakpoint; exports.getBreakpoints = getBreakpoints; exports.getBreakpointsForSource = getBreakpointsForSource; @@ -14355,8 +14372,18 @@ return /******/ (function(modules) { // webpackBootstrap line = location.line, column = location.column; - column = column || ""; - return `${sourceId}:${line}:${column}`; + var columnString = column || ""; + return `${sourceId}:${line}:${columnString}`; + } + + function makePendingLocationId(location) { + var sourceUrl = location.sourceUrl, + line = location.line, + column = location.column; + + var sourceUrlString = sourceUrl || ""; + var columnString = column || ""; + return `${sourceUrlString}:${line}:${columnString}`; } function allBreakpointsDisabled(state) { @@ -14371,15 +14398,13 @@ return /******/ (function(modules) { // webpackBootstrap case "ADD_BREAKPOINT": { var newState = addBreakpoint(state, action); - if (newState) { - setPendingBreakpoints(newState); - } + setPendingBreakpoints(newState); return newState; } case "REMOVE_BREAKPOINT": { - var _newState = removeBreakpoint(state, action); + var _newState = removeOrDisableBreakpoint(state, action); setPendingBreakpoints(_newState); return _newState; } @@ -14394,25 +14419,9 @@ return /******/ (function(modules) { // webpackBootstrap case "SET_BREAKPOINT_CONDITION": { - var id = makeLocationId(action.breakpoint.location); - - if (action.status === "start") { - var bp = state.breakpoints.get(id); - return state.setIn(["breakpoints", id], (0, _utils.updateObj)(bp, { - loading: true, - condition: action.condition - })); - } else if (action.status === "done") { - var _bp = state.breakpoints.get(id); - return state.setIn(["breakpoints", id], (0, _utils.updateObj)(_bp, { - id: action.value.id, - loading: false - })); - } else if (action.status === "error") { - return state.deleteIn(["breakpoints", id]); - } - - break; + var _newState2 = setCondition(state, action); + setPendingBreakpoints(_newState2); + return _newState2; } } @@ -14421,11 +14430,10 @@ return /******/ (function(modules) { // webpackBootstrap function addBreakpoint(state, action) { var id = makeLocationId(action.breakpoint.location); - if (action.status === "start") { var bp = state.breakpoints.get(id) || action.breakpoint; - return state.setIn(["breakpoints", id], (0, _utils.updateObj)(bp, { + var updatedState = state.setIn(["breakpoints", id], (0, _utils.updateObj)(bp, { disabled: false, loading: true, // We want to do an OR here, but we can't because we need @@ -14433,6 +14441,8 @@ return /******/ (function(modules) { // webpackBootstrap // condition. condition: firstString(action.condition, bp.condition) })).set("breakpointsDisabled", false); + + return updatedState; } if (action.status === "done") { @@ -14446,23 +14456,20 @@ return /******/ (function(modules) { // webpackBootstrap // If the breakpoint moved, update the map if (locationMoved(location, actualLocation)) { - state = state.deleteIn(["breakpoints", id]); - - var movedId = makeLocationId(actualLocation); - var currentBp = state.breakpoints.get(movedId) || (0, _fromJS2.default)(action.breakpoint); - var newBp = (0, _utils.updateObj)(currentBp, { location: actualLocation }); - state = state.setIn(["breakpoints", movedId], newBp); + state = slideBreakpoint(state, action); location = actualLocation; } var locationId = makeLocationId(location); - var _bp2 = state.breakpoints.get(locationId); - return state.setIn(["breakpoints", locationId], (0, _utils.updateObj)(_bp2, { + var _bp = state.breakpoints.get(locationId); + var _updatedState = state.setIn(["breakpoints", locationId], (0, _utils.updateObj)(_bp, { id: breakpointId, disabled: false, loading: false, text: text })); + + return updatePendingBreakpoint(_updatedState, _bp); } if (action.status === "error") { @@ -14471,26 +14478,74 @@ return /******/ (function(modules) { // webpackBootstrap } } - function removeBreakpoint(state, action) { - if (action.status === "done") { - var id = makeLocationId(action.breakpoint.location); + function disableBreakpoint(state, id) { + var bp = state.breakpoints.get(id); + var breakpoint = (0, _utils.updateObj)(bp, { + loading: false, + disabled: true + }); + var updatedState = state.setIn(["breakpoints", id], breakpoint); + return updatePendingBreakpoint(updatedState, breakpoint); + } - if (action.disabled) { - var bp = state.breakpoints.get(id); - var _updatedState = state.setIn(["breakpoints", id], (0, _utils.updateObj)(bp, { - loading: false, - disabled: true - })); + function deleteBreakpoint(state, id, pendingId) { + return state.deleteIn(["breakpoints", id]).deleteIn(["pendingBreakpoints", pendingId]); + } - return _updatedState.set("breakpointsDisabled", allBreakpointsDisabled(_updatedState)); - } + function removeOrDisableBreakpoint(state, action) { + if (action.status != "done") { + return state; + } + + var id = makeLocationId(action.breakpoint.location); + var pendingId = makePendingLocationId(action.breakpoint.location); + + var updatedState = action.disabled ? disableBreakpoint(state, id) : deleteBreakpoint(state, id, pendingId); + + return updatedState.set("breakpointsDisabled", allBreakpointsDisabled(updatedState)); + } - var updatedState = state.deleteIn(["breakpoints", id]); + function setCondition(state, action) { + var id = makeLocationId(action.breakpoint.location); - return updatedState.set("breakpointsDisabled", allBreakpointsDisabled(updatedState)); + if (action.status === "start") { + var bp = state.breakpoints.get(id); + return state.setIn(["breakpoints", id], (0, _utils.updateObj)(bp, { + loading: true, + condition: action.condition + })); } - return state; + if (action.status === "done") { + var _bp2 = state.breakpoints.get(id); + var updatedBreakpoint = (0, _utils.updateObj)(_bp2, { + id: action.value.id, + loading: false + }); + + var updatedState = state.setIn(["breakpoints", id], updatedBreakpoint); + + return updatePendingBreakpoint(updatedState, updatedBreakpoint); + } + + if (action.status === "error") { + return state.deleteIn(["breakpoints", id]); + } + } + + function slideBreakpoint(state, action) { + var _action$value2 = action.value, + actualLocation = _action$value2.actualLocation, + id = _action$value2.id; + var breakpoint = action.breakpoint; + + var currentBp = state.breakpoints.get(id) || (0, _fromJS2.default)(breakpoint); + + var locationId = makeLocationId(breakpoint.location); + var movedLocationId = makeLocationId(actualLocation); + var updatedState = state.deleteIn(["breakpoints", locationId]); + + return updatedState.setIn(["breakpoints", movedLocationId], (0, _utils.updateObj)(currentBp, { location: actualLocation })); } function makePendingBreakpoint(bp) { @@ -14506,16 +14561,17 @@ return /******/ (function(modules) { // webpackBootstrap return { condition, disabled, location }; } - function filterByNotLoading(bp) { - return !bp.loading; + function setPendingBreakpoints(state) { + _prefs.prefs.pendingBreakpoints = state.pendingBreakpoints; } - function setPendingBreakpoints(state) { - _prefs.prefs.pendingBreakpoints = Object.values(state.get("breakpoints").toJS()).filter(filterByNotLoading).map(makePendingBreakpoint); + function updatePendingBreakpoint(state, breakpoint) { + var id = makePendingLocationId(breakpoint.location); + return state.setIn(["pendingBreakpoints", id], makePendingBreakpoint(breakpoint)); } function restorePendingBreakpoints() { - return _prefs.prefs.pendingBreakpoints; + return I.Map(_prefs.prefs.pendingBreakpoints); } // Selectors @@ -14557,13 +14613,39 @@ return /******/ (function(modules) { // webpackBootstrap "use strict"; + var _immutable = __webpack_require__(146); + + var I = _interopRequireWildcard(_immutable); + + var _isFunction = __webpack_require__(83); + + var _isFunction2 = _interopRequireDefault(_isFunction); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + + function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } + + // hasOwnProperty is defensive because it is possible that the + // object that we're creating a map for has a `hasOwnProperty` field + + /** * Immutable JS conversion utils * @deprecated * @module utils/fromJS */ - var Immutable = __webpack_require__(146); + function hasOwnProperty(value, key) { + if (value.hasOwnProperty && (0, _isFunction2.default)(value.hasOwnProperty)) { + return value.hasOwnProperty(key); + } + + if (value.prototype && value.prototype.hasOwnProperty) { + return value.prototype.hasOwnProperty(key); + } + + return false; + } /* creates an immutable map, where each of the value's @@ -14573,14 +14655,14 @@ return /******/ (function(modules) { // webpackBootstrap length confuses Immutable's internal algorithm. */ function createMap(value) { - var hasLength = value.hasOwnProperty && value.hasOwnProperty("length"); + var hasLength = hasOwnProperty(value, "length"); var length = value.length; if (hasLength) { value.length = `${value.length}`; } - var map = Immutable.Seq(value).map(fromJS).toMap(); + var map = I.Seq(value).map(fromJS).toMap(); if (hasLength) { map = map.set("length", length); @@ -14591,7 +14673,7 @@ return /******/ (function(modules) { // webpackBootstrap } function createList(value) { - return Immutable.Seq(value).map(fromJS).toList(); + return I.Seq(value).map(fromJS).toList(); } /** @@ -14701,7 +14783,7 @@ return /******/ (function(modules) { // webpackBootstrap exports.getDebuggeeUrl = getDebuggeeUrl; exports.getChromeScopes = getChromeScopes; - var _reselect = __webpack_require__(992); + var _reselect = __webpack_require__(993); var _fromJS = __webpack_require__(237); @@ -14915,6 +14997,7 @@ return /******/ (function(modules) { // webpackBootstrap exports.getSymbolSearchType = getSymbolSearchType; exports.getShownSource = getShownSource; exports.getPaneCollapse = getPaneCollapse; + exports.getHighlightedLineRange = getHighlightedLineRange; var _makeRecord = __webpack_require__(230); @@ -14941,7 +15024,8 @@ return /******/ (function(modules) { // webpackBootstrap symbolSearchType: "functions", shownSource: "", startPanelCollapsed: _prefs.prefs.startPanelCollapsed, - endPanelCollapsed: _prefs.prefs.endPanelCollapsed + endPanelCollapsed: _prefs.prefs.endPanelCollapsed, + highlightedLineRange: undefined }); /** @@ -15000,6 +15084,23 @@ return /******/ (function(modules) { // webpackBootstrap return state.set("endPanelCollapsed", action.paneCollapsed); } + case "HIGHLIGHT_LINES": + var _action$location = action.location, + _start = _action$location.start, + _end = _action$location.end, + _sourceId = _action$location.sourceId; + + var lineRange = {}; + + if (_start && _end && _sourceId) { + lineRange = { start: _start, end: _end, sourceId: _sourceId }; + } + + return state.set("highlightedLineRange", lineRange); + + case "CLEAR_HIGHLIGHT_LINES": + return state.set("highlightedLineRange", {}); + default: { return state; @@ -15042,6 +15143,10 @@ return /******/ (function(modules) { // webpackBootstrap return state.ui.get("endPanelCollapsed"); } + function getHighlightedLineRange(state) { + return state.ui.get("highlightedLineRange"); + } + exports.default = update; /***/ }, @@ -15181,7 +15286,8 @@ return /******/ (function(modules) { // webpackBootstrap getExpressions: expressions.getExpressions, getVisibleExpressions: expressions.getVisibleExpressions, - getExpression: expressions.getExpression + getExpression: expressions.getExpression, + getHighlightedLineRange: ui.getHighlightedLineRange }; /***/ }, @@ -15302,14 +15408,15 @@ return /******/ (function(modules) { // webpackBootstrap endPanelCollapsed = _props.endPanelCollapsed; var _state = this.state, horizontal = _state.horizontal, - endPanelSize = _state.endPanelSize; + endPanelSize = _state.endPanelSize, + startPanelSize = _state.startPanelSize; return _react.DOM.div({ className: "editor-pane" }, _react.DOM.div({ className: "editor-container" }, EditorTabs({ startPanelCollapsed, endPanelCollapsed, horizontal, endPanelSize - }), Editor({ horizontal }), !this.props.selectedSource ? WelcomeBox({ horizontal }) : null, ProjectSearch())); + }), Editor({ horizontal, startPanelSize, endPanelSize }), !this.props.selectedSource ? WelcomeBox({ horizontal }) : null, ProjectSearch())); } renderHorizontalLayout() { @@ -15380,13 +15487,6 @@ return /******/ (function(modules) { // webpackBootstrap } } - App.propTypes = { - selectSource: _react.PropTypes.func, - selectedSource: _react.PropTypes.object, - startPanelCollapsed: _react.PropTypes.bool, - endPanelCollapsed: _react.PropTypes.bool - }; - App.displayName = "App"; App.childContextTypes = { shortcuts: _react.PropTypes.object }; @@ -15550,7 +15650,6 @@ return /******/ (function(modules) { // webpackBootstrap if (!text) { text = getTextForLine ? getTextForLine(actualLocation.line) : ""; } - return { id, actualLocation, text, hitCount }; })() }); @@ -15577,7 +15676,9 @@ return /******/ (function(modules) { // webpackBootstrap return _removeOrDisableBreakpoint(location); } - function _removeOrDisableBreakpoint(location, isDisabled) { + function _removeOrDisableBreakpoint(location) { + var isDisabled = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; + return (_ref5) => { var dispatch = _ref5.dispatch, getState = _ref5.getState, @@ -16408,6 +16509,57 @@ return /******/ (function(modules) { // webpackBootstrap var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }(); + var checkPendingBreakpoint = (() => { + var _ref = _asyncToGenerator(function* (state, dispatch, pendingBreakpoint, source) { + var _pendingBreakpoint$lo = pendingBreakpoint.location, + line = _pendingBreakpoint$lo.line, + sourceUrl = _pendingBreakpoint$lo.sourceUrl, + column = _pendingBreakpoint$lo.column, + condition = pendingBreakpoint.condition; + + var sameSource = sourceUrl && sourceUrl === source.url; + var location = { sourceId: source.id, sourceUrl, line, column }; + var bp = (0, _selectors.getBreakpoint)(state, location); + + if (sameSource && !bp) { + if (location.column && (0, _devtoolsConfig.isEnabled)("columnBreakpoints")) { + yield dispatch((0, _breakpoints.addBreakpoint)(location, { condition })); + } else { + yield dispatch((0, _breakpoints.addBreakpoint)(location, { condition })); + } + } + }); + + return function checkPendingBreakpoint(_x, _x2, _x3, _x4) { + return _ref.apply(this, arguments); + }; + })(); + + var checkPendingBreakpoints = (() => { + var _ref2 = _asyncToGenerator(function* (state, dispatch, source) { + var pendingBreakpoints = (0, _selectors.getPendingBreakpoints)(state); + if (!pendingBreakpoints) { + return; + } + + var pendingBreakpointsArray = pendingBreakpoints.valueSeq().toJS(); + for (var pendingBreakpoint of pendingBreakpointsArray) { + yield checkPendingBreakpoint(state, dispatch, pendingBreakpoint, source); + } + }); + + return function checkPendingBreakpoints(_x5, _x6, _x7) { + return _ref2.apply(this, arguments); + }; + })(); + + /** + * Handler for the debugger client's unsolicited newSource notification. + * @memberof actions/sources + * @static + */ + + exports.newSource = newSource; exports.newSources = newSources; exports.selectSourceURL = selectSourceURL; @@ -16467,67 +16619,53 @@ return /******/ (function(modules) { // webpackBootstrap // select it. function checkSelectedSource(state, dispatch, source) { var pendingLocation = (0, _selectors.getPendingSelectedLocation)(state); + if (pendingLocation && pendingLocation.url === source.url) { dispatch(selectSource(source.id, { line: pendingLocation.line })); } } - function checkPendingBreakpoints(state, dispatch, source) { - var pendingBreakpoints = (0, _selectors.getPendingBreakpoints)(state); - - if (pendingBreakpoints) { - pendingBreakpoints.forEach(pendingBreakpoint => { - var _pendingBreakpoint$lo = pendingBreakpoint.location, - line = _pendingBreakpoint$lo.line, - sourceUrl = _pendingBreakpoint$lo.sourceUrl, - column = _pendingBreakpoint$lo.column, - condition = pendingBreakpoint.condition; - - var sameSource = sourceUrl && sourceUrl == source.url; - - var location = { sourceId: source.id, sourceUrl, line, column }; + function newSource(source) { + return (() => { + var _ref3 = _asyncToGenerator(function* (_ref4) { + var dispatch = _ref4.dispatch, + getState = _ref4.getState; - var bp = (0, _selectors.getBreakpoint)(state, location); + dispatch({ type: _constants2.default.ADD_SOURCE, source }); - if (sameSource && !bp) { - if (location.column && (0, _devtoolsConfig.isEnabled)("columnBreakpoints")) { - dispatch((0, _breakpoints.addBreakpoint)(location, { condition })); - } else { - dispatch((0, _breakpoints.addBreakpoint)(location, { condition })); - } + if (_prefs.prefs.clientSourceMapsEnabled) { + yield dispatch(loadSourceMap(source)); } - }); - } - } - - /** - * Handler for the debugger client's unsolicited newSource notification. - * @memberof actions/sources - * @static - */ - function newSource(source) { - return (_ref) => { - var dispatch = _ref.dispatch, - getState = _ref.getState; - - if (_prefs.prefs.clientSourceMapsEnabled) { - dispatch(loadSourceMap(source)); - } - dispatch({ type: _constants2.default.ADD_SOURCE, source }); + checkSelectedSource(getState(), dispatch, source); + yield checkPendingBreakpoints(getState(), dispatch, source); + }); - checkSelectedSource(getState(), dispatch, source); - checkPendingBreakpoints(getState(), dispatch, source); - }; + return function (_x8) { + return _ref3.apply(this, arguments); + }; + })(); } function newSources(sources) { - return (_ref2) => { - var dispatch = _ref2.dispatch, - getState = _ref2.getState; + return (() => { + var _ref5 = _asyncToGenerator(function* (_ref6) { + var dispatch = _ref6.dispatch, + getState = _ref6.getState; - sources.filter(source => !(0, _selectors.getSource)(getState(), source.id)).forEach(source => dispatch(newSource(source))); - }; + var filteredSources = sources.filter(function (source) { + return !(0, _selectors.getSource)(getState(), source.id); + }); + + for (var source of filteredSources) { + yield dispatch(newSource(source)); + } + }); + + return function (_x9) { + return _ref5.apply(this, arguments); + }; + })(); } /** @@ -16536,10 +16674,10 @@ return /******/ (function(modules) { // webpackBootstrap */ function loadSourceMap(generatedSource) { return (() => { - var _ref3 = _asyncToGenerator(function* (_ref4) { - var dispatch = _ref4.dispatch, - getState = _ref4.getState, - sourceMaps = _ref4.sourceMaps; + var _ref7 = _asyncToGenerator(function* (_ref8) { + var dispatch = _ref8.dispatch, + getState = _ref8.getState, + sourceMaps = _ref8.sourceMaps; var urls = yield sourceMaps.getOriginalURLs(generatedSource); if (!urls) { @@ -16564,8 +16702,8 @@ return /******/ (function(modules) { // webpackBootstrap }); }); - return function (_x) { - return _ref3.apply(this, arguments); + return function (_x10) { + return _ref7.apply(this, arguments); }; })(); } @@ -16582,9 +16720,9 @@ return /******/ (function(modules) { // webpackBootstrap function selectSourceURL(url) { var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; - return (_ref5) => { - var dispatch = _ref5.dispatch, - getState = _ref5.getState; + return (_ref9) => { + var dispatch = _ref9.dispatch, + getState = _ref9.getState; var source = (0, _selectors.getSourceByURL)(getState(), url); if (source) { @@ -16607,10 +16745,10 @@ return /******/ (function(modules) { // webpackBootstrap function selectSource(id) { var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; - return (_ref6) => { - var dispatch = _ref6.dispatch, - getState = _ref6.getState, - client = _ref6.client; + return (_ref10) => { + var dispatch = _ref10.dispatch, + getState = _ref10.getState, + client = _ref10.client; if (!client) { // No connection, do nothing. This happens when the debugger is @@ -16646,11 +16784,11 @@ return /******/ (function(modules) { // webpackBootstrap */ function jumpToMappedLocation(sourceLocation) { return (() => { - var _ref7 = _asyncToGenerator(function* (_ref8) { - var dispatch = _ref8.dispatch, - getState = _ref8.getState, - client = _ref8.client, - sourceMaps = _ref8.sourceMaps; + var _ref11 = _asyncToGenerator(function* (_ref12) { + var dispatch = _ref12.dispatch, + getState = _ref12.getState, + client = _ref12.client, + sourceMaps = _ref12.sourceMaps; if (!client) { return; @@ -16667,8 +16805,8 @@ return /******/ (function(modules) { // webpackBootstrap return dispatch(selectSource(pairedLocation.sourceId, { line: pairedLocation.line })); }); - return function (_x4) { - return _ref7.apply(this, arguments); + return function (_x13) { + return _ref11.apply(this, arguments); }; })(); } @@ -16687,10 +16825,10 @@ return /******/ (function(modules) { // webpackBootstrap * @static */ function closeTabs(urls) { - return (_ref9) => { - var dispatch = _ref9.dispatch, - getState = _ref9.getState, - client = _ref9.client; + return (_ref13) => { + var dispatch = _ref13.dispatch, + getState = _ref13.getState, + client = _ref13.client; urls.forEach(url => { var source = (0, _selectors.getSourceByURL)(getState(), url); @@ -16716,11 +16854,11 @@ return /******/ (function(modules) { // webpackBootstrap * [aSource, error]. */ function togglePrettyPrint(sourceId) { - return (_ref10) => { - var dispatch = _ref10.dispatch, - getState = _ref10.getState, - client = _ref10.client, - sourceMaps = _ref10.sourceMaps; + return (_ref14) => { + var dispatch = _ref14.dispatch, + getState = _ref14.getState, + client = _ref14.client, + sourceMaps = _ref14.sourceMaps; var source = (0, _selectors.getSource)(getState(), sourceId).toJS(); var sourceText = (0, _selectors.getSourceText)(getState(), sourceId); @@ -16743,13 +16881,13 @@ return /******/ (function(modules) { // webpackBootstrap type: _constants2.default.TOGGLE_PRETTY_PRINT, source: originalSource, [_promise.PROMISE]: _asyncToGenerator(function* () { - var _ref12 = yield (0, _prettyPrint.prettyPrint)({ + var _ref16 = yield (0, _prettyPrint.prettyPrint)({ source, sourceText, url }), - code = _ref12.code, - mappings = _ref12.mappings; + code = _ref16.code, + mappings = _ref16.mappings; yield sourceMaps.applySourceMap(source.id, url, code, mappings); @@ -16768,11 +16906,11 @@ return /******/ (function(modules) { // webpackBootstrap function toggleBlackBox(source) { return (() => { - var _ref13 = _asyncToGenerator(function* (_ref14) { - var dispatch = _ref14.dispatch, - getState = _ref14.getState, - client = _ref14.client, - sourceMaps = _ref14.sourceMaps; + var _ref17 = _asyncToGenerator(function* (_ref18) { + var dispatch = _ref18.dispatch, + getState = _ref18.getState, + client = _ref18.client, + sourceMaps = _ref18.sourceMaps; var isBlackBoxed = source.isBlackBoxed, id = source.id; @@ -16784,8 +16922,8 @@ return /******/ (function(modules) { // webpackBootstrap }); }); - return function (_x5) { - return _ref13.apply(this, arguments); + return function (_x14) { + return _ref17.apply(this, arguments); }; })(); } @@ -16795,11 +16933,11 @@ return /******/ (function(modules) { // webpackBootstrap * @static */ function loadSourceText(source) { - return (_ref15) => { - var dispatch = _ref15.dispatch, - getState = _ref15.getState, - client = _ref15.client, - sourceMaps = _ref15.sourceMaps; + return (_ref19) => { + var dispatch = _ref19.dispatch, + getState = _ref19.getState, + client = _ref19.client, + sourceMaps = _ref19.sourceMaps; // Fetch the source text only once. var textInfo = (0, _selectors.getSourceText)(getState(), source.id); @@ -16852,9 +16990,9 @@ return /******/ (function(modules) { // webpackBootstrap * A promise that is resolved after source texts have been fetched. */ function getTextForSources(actors) { - return (_ref17) => { - var dispatch = _ref17.dispatch, - getState = _ref17.getState; + return (_ref21) => { + var dispatch = _ref21.dispatch, + getState = _ref21.getState; var deferred = (0, _defer2.default)(); var pending = new Set(actors); @@ -16869,9 +17007,9 @@ return /******/ (function(modules) { // webpackBootstrap var _loop = function (actor) { var source = (0, _selectors.getSource)(getState(), actor); - dispatch(loadSourceText(source)).then((_ref26) => { - var text = _ref26.text, - contentType = _ref26.contentType; + dispatch(loadSourceText(source)).then((_ref30) => { + var text = _ref30.text, + contentType = _ref30.contentType; onFetch([source, text, contentType]); }, err => { @@ -16892,11 +17030,11 @@ return /******/ (function(modules) { // webpackBootstrap } /* Called if fetching a source finishes successfully. */ - function onFetch(_ref18) { - var _ref19 = _slicedToArray(_ref18, 3), - aSource = _ref19[0], - aText = _ref19[1], - aContentType = _ref19[2]; + function onFetch(_ref22) { + var _ref23 = _slicedToArray(_ref22, 3), + aSource = _ref23[0], + aText = _ref23[1], + aContentType = _ref23[2]; // If fetching the source has previously timed out, discard it this time. if (!pending.has(aSource.actor)) { @@ -16908,10 +17046,10 @@ return /******/ (function(modules) { // webpackBootstrap } /* Called if fetching a source failed because of an error. */ - function onError(_ref20) { - var _ref21 = _slicedToArray(_ref20, 2), - aSource = _ref21[0], - aError = _ref21[1]; + function onError(_ref24) { + var _ref25 = _slicedToArray(_ref24, 2), + aSource = _ref25[0], + aError = _ref25[1]; pending.delete(aSource.actor); maybeFinish(); @@ -16924,12 +17062,12 @@ return /******/ (function(modules) { // webpackBootstrap if (pending.size == 0) { // Sort the fetched sources alphabetically by their url. if (deferred) { - deferred.resolve(fetched.sort((_ref22, _ref23) => { - var _ref25 = _slicedToArray(_ref22, 1), - aFirst = _ref25[0]; + deferred.resolve(fetched.sort((_ref26, _ref27) => { + var _ref29 = _slicedToArray(_ref26, 1), + aFirst = _ref29[0]; - var _ref24 = _slicedToArray(_ref23, 1), - aSecond = _ref24[0]; + var _ref28 = _slicedToArray(_ref27, 1), + aSecond = _ref28[0]; return aFirst > aSecond ? -1 : 1; })); @@ -17030,7 +17168,7 @@ return /******/ (function(modules) { // webpackBootstrap var sourceSearchUtils = _interopRequireWildcard(_sourceSearch); - var _devtoolsSourceEditor = __webpack_require__(993); + var _devtoolsSourceEditor = __webpack_require__(994); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } @@ -19651,6 +19789,8 @@ return /******/ (function(modules) { // webpackBootstrap exports.toggleFileSearchModifier = toggleFileSearchModifier; exports.showSource = showSource; exports.togglePaneCollapse = togglePaneCollapse; + exports.highlightLineRange = highlightLineRange; + exports.clearHighlightLineRange = clearHighlightLineRange; var _constants = __webpack_require__(229); @@ -19665,17 +19805,22 @@ return /******/ (function(modules) { // webpackBootstrap var dispatch = _ref.dispatch, getState = _ref.getState; - if (toggleValue != null) { - dispatch({ - type: _constants2.default.TOGGLE_PROJECT_SEARCH, - value: toggleValue - }); - } else { - dispatch({ + var projectSearchState = (0, _selectors.getProjectSearchState)(getState()); + if (toggleValue === undefined) { + return dispatch({ type: _constants2.default.TOGGLE_PROJECT_SEARCH, - value: !(0, _selectors.getProjectSearchState)(getState()) + value: !projectSearchState }); } + + if (projectSearchState == toggleValue) { + return; + } + + dispatch({ + type: _constants2.default.TOGGLE_PROJECT_SEARCH, + value: toggleValue + }); }; } @@ -19754,6 +19899,27 @@ return /******/ (function(modules) { // webpackBootstrap }; } + /** + * @memberof actions/sources + * @static + */ + function highlightLineRange(location) { + return { + type: _constants2.default.HIGHLIGHT_LINES, + location + }; + } + + /** + * @memberof actions/sources + * @static + */ + function clearHighlightLineRange() { + return { + type: _constants2.default.CLEAR_HIGHLIGHT_LINES + }; + } + /***/ }, /* 322 */ /***/ function(module, exports, __webpack_require__) { @@ -21044,16 +21210,17 @@ return /******/ (function(modules) { // webpackBootstrap var svg = { "angle-brackets": __webpack_require__(347), arrow: __webpack_require__(348), - backbone: __webpack_require__(996), + backbone: __webpack_require__(997), blackBox: __webpack_require__(349), breakpoint: __webpack_require__(350), + "column-breakpoint": __webpack_require__(998), "case-match": __webpack_require__(351), close: __webpack_require__(352), domain: __webpack_require__(353), file: __webpack_require__(354), folder: __webpack_require__(355), globe: __webpack_require__(356), - jquery: __webpack_require__(997), + jquery: __webpack_require__(999), "magnifying-glass": __webpack_require__(357), "arrow-up": __webpack_require__(919), "arrow-down": __webpack_require__(920), @@ -21061,7 +21228,7 @@ return /******/ (function(modules) { // webpackBootstrap "pause-exceptions": __webpack_require__(359), plus: __webpack_require__(360), prettyPrint: __webpack_require__(361), - react: __webpack_require__(998), + react: __webpack_require__(1000), "regex-match": __webpack_require__(362), resume: __webpack_require__(363), settings: __webpack_require__(364), @@ -21075,7 +21242,10 @@ return /******/ (function(modules) { // webpackBootstrap worker: __webpack_require__(372), "sad-face": __webpack_require__(373), refresh: __webpack_require__(374), - webpack: __webpack_require__(999) + webpack: __webpack_require__(1001), + node: __webpack_require__(1002), + express: __webpack_require__(1003), + pug: __webpack_require__(1004) }; module.exports = function (name, props) { @@ -21747,7 +21917,7 @@ return /******/ (function(modules) { // webpackBootstrap className: "sources-header-info", dir: "ltr", onClick: () => this.props.toggleProjectSearch() - }, L10N.getFormatStr("sources.search", (0, _text.formatKeyShortcut)(`CmdOrCtrl+${L10N.getStr("sources.search.key")}`))); + }, L10N.getFormatStr("sources.search", (0, _text.formatKeyShortcut)(L10N.getStr("sources.search.key2")))); } } @@ -21757,7 +21927,7 @@ return /******/ (function(modules) { // webpackBootstrap selectSource = _props.selectSource; - return _react.DOM.div({ className: "sources-panel" }, _react.DOM.div({ className: "sources-header" }, this.renderShortcut()), SourcesTree({ sources, selectSource }), Outline({})); + return _react.DOM.div({ className: "sources-panel" }, _react.DOM.div({ className: "sources-header" }, this.renderShortcut()), SourcesTree({ sources, selectSource }), Outline({ selectSource })); } } @@ -23478,7 +23648,7 @@ return /******/ (function(modules) { // webpackBootstrap __webpack_require__(420); - var _devtoolsComponents = __webpack_require__(1000); + var _devtoolsComponents = __webpack_require__(1007); var Tree = (0, _react.createFactory)(_devtoolsComponents.Tree); @@ -23510,19 +23680,6 @@ return /******/ (function(modules) { // webpackBootstrap } } - componentWillMount() { - if (this.props.getExpanded) { - var _expanded = this.props.getExpanded(); - this.setState({ expanded: _expanded }); - } - } - - componentWillUnmount() { - if (this.props.setExpanded) { - this.props.setExpanded(this.state.expanded); - } - } - setExpanded(item, isExpanded) { var expanded = this.state.expanded; var key = this.props.getKey(item); @@ -23606,10 +23763,7 @@ return /******/ (function(modules) { // webpackBootstrap ManagedTree.displayName = "ManagedTree"; - ManagedTree.propTypes = Object.assign({}, Tree.propTypes, { - getExpanded: _react.PropTypes.func, - setExpanded: _react.PropTypes.func - }); + ManagedTree.propTypes = Object.assign({}, Tree.propTypes); exports.default = ManagedTree; @@ -23627,6 +23781,9 @@ return /******/ (function(modules) { // webpackBootstrap "use strict"; + Object.defineProperty(exports, "__esModule", { + value: true + }); /** * Clipboard function taken from * https://dxr.mozilla.org/mozilla-central/source/devtools/shared/platform/content/clipboard.js @@ -23642,7 +23799,7 @@ return /******/ (function(modules) { // webpackBootstrap document.removeEventListener("copy", doCopy); } - module.exports = { copyToTheClipboard }; + exports.copyToTheClipboard = copyToTheClipboard; /***/ }, /* 424 */ @@ -23675,7 +23832,7 @@ return /******/ (function(modules) { // webpackBootstrap var _reactRedux = __webpack_require__(151); - var _reselect = __webpack_require__(992); + var _reselect = __webpack_require__(993); var _classnames = __webpack_require__(175); @@ -23717,6 +23874,10 @@ return /******/ (function(modules) { // webpackBootstrap var _SearchBar3 = _interopRequireDefault(_SearchBar2); + var _HighlightLines2 = __webpack_require__(1025); + + var _HighlightLines3 = _interopRequireDefault(_HighlightLines2); + var _Preview2 = __webpack_require__(657); var _Preview3 = _interopRequireDefault(_Preview2); @@ -23725,7 +23886,7 @@ return /******/ (function(modules) { // webpackBootstrap var _Breakpoint3 = _interopRequireDefault(_Breakpoint2); - var _ColumnBreakpoint2 = __webpack_require__(1004); + var _ColumnBreakpoint2 = __webpack_require__(1011); var _ColumnBreakpoint3 = _interopRequireDefault(_ColumnBreakpoint2); @@ -23739,6 +23900,8 @@ return /******/ (function(modules) { // webpackBootstrap __webpack_require__(716); + var _devtoolsSourceEditor = __webpack_require__(994); + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; } @@ -23747,6 +23910,8 @@ return /******/ (function(modules) { // webpackBootstrap var SearchBar = (0, _react.createFactory)(_SearchBar3.default); + var HighlightLines = (0, _react.createFactory)(_HighlightLines3.default); + var Preview = (0, _react.createFactory)(_Preview3.default); var Breakpoint = (0, _react.createFactory)(_Breakpoint3.default); @@ -23776,6 +23941,7 @@ return /******/ (function(modules) { // webpackBootstrap index: -1, count: 0 }, + highlightedLineRange: null, selectedToken: null, selectedExpression: null }; @@ -23804,6 +23970,10 @@ return /******/ (function(modules) { // webpackBootstrap this.clearDebugLine(this.props.selectedFrame); + if (nextProps.startPanelSize !== this.props.startPanelSize || nextProps.endPanelSize !== this.props.endPanelSize) { + this.editor.codeMirror.setSize(); + } + if (!sourceText) { if (this.props.sourceText) { this.showMessage(""); @@ -23879,13 +24049,14 @@ return /******/ (function(modules) { // webpackBootstrap var shortcuts = this.context.shortcuts; - var searchAgainKey = L10N.getStr("sourceSearch.search.again.key"); + var searchAgainKey = L10N.getStr("sourceSearch.search.again.key2"); + var searchAgainPrevKey = L10N.getStr("sourceSearch.search.againPrev.key2"); shortcuts.on("CmdOrCtrl+B", this.onToggleBreakpoint); shortcuts.on("CmdOrCtrl+Shift+B", this.onToggleBreakpoint); shortcuts.on("Esc", this.onEscape); - shortcuts.on(`CmdOrCtrl+Shift+${searchAgainKey}`, this.onSearchAgain); - shortcuts.on(`CmdOrCtrl+${searchAgainKey}`, this.onSearchAgain); + shortcuts.on(searchAgainPrevKey, this.onSearchAgain); + shortcuts.on(searchAgainKey, this.onSearchAgain); (0, _editor.updateDocument)(this.editor, selectedSource, sourceText); } @@ -23894,12 +24065,13 @@ return /******/ (function(modules) { // webpackBootstrap this.editor.destroy(); this.editor = null; - var searchAgainKey = L10N.getStr("sourceSearch.search.again.key"); + var searchAgainKey = L10N.getStr("sourceSearch.search.again.key2"); + var searchAgainPrevKey = L10N.getStr("sourceSearch.search.againPrev.key2"); var shortcuts = this.context.shortcuts; shortcuts.off("CmdOrCtrl+B"); shortcuts.off("CmdOrCtrl+Shift+B"); - shortcuts.off(`CmdOrCtrl+Shift+${searchAgainKey}`); - shortcuts.off(`CmdOrCtrl+${searchAgainKey}`); + shortcuts.off(searchAgainPrevKey); + shortcuts.off(searchAgainKey); } componentDidUpdate(prevProps) { @@ -24253,14 +24425,14 @@ return /******/ (function(modules) { // webpackBootstrap clearDebugLine(selectedFrame) { if (selectedFrame) { var line = selectedFrame.location.line; - this.editor.codeMirror.removeLineClass(line - 1, "line", "debug-line"); + this.editor.codeMirror.removeLineClass(line - 1, "line", "new-debug-line"); } } setDebugLine(selectedFrame, selectedLocation) { if (selectedFrame && selectedLocation && selectedFrame.location.sourceId === selectedLocation.sourceId) { var line = selectedFrame.location.line; - this.editor.codeMirror.addLineClass(line - 1, "line", "debug-line"); + this.editor.codeMirror.addLineClass(line - 1, "line", "new-debug-line"); } } @@ -24332,6 +24504,20 @@ return /******/ (function(modules) { // webpackBootstrap this.editor.setMode((0, _source.getMode)(sourceText.toJS())); } + renderHighlightLines() { + var highlightedLineRange = this.props.highlightedLineRange; + + + if (!highlightedLineRange) { + return; + } + + return HighlightLines({ + editor: this.editor, + highlightedLineRange + }); + } + renderBreakpoints() { var _props7 = this.props, breakpoints = _props7.breakpoints, @@ -24418,13 +24604,12 @@ return /******/ (function(modules) { // webpackBootstrap } var token = selectedToken.textContent; - selectedToken.classList.add("selected-token"); var value = (0, _editor.getExpressionValue)(selectedExpression, { getExpression: this.props.getExpression }); - if (!value) { + if (!value || value.type == "undefined") { return; } @@ -24433,7 +24618,6 @@ return /******/ (function(modules) { // webpackBootstrap expression: token, popoverTarget: selectedToken, onClose: () => { - selectedToken.classList.remove("selected-token"); this.setState({ selectedToken: null, selectedExpression: null @@ -24447,6 +24631,8 @@ return /******/ (function(modules) { // webpackBootstrap sourceText = _props11.sourceText, selectSource = _props11.selectSource, selectedSource = _props11.selectedSource, + highlightLineRange = _props11.highlightLineRange, + clearHighlightLineRange = _props11.clearHighlightLineRange, coverageOn = _props11.coverageOn, horizontal = _props11.horizontal; var searchResults = this.state.searchResults; @@ -24458,13 +24644,15 @@ return /******/ (function(modules) { // webpackBootstrap editor: this.editor, selectSource, selectedSource, + highlightLineRange, + clearHighlightLineRange, sourceText, searchResults, updateSearchResults: this.updateSearchResults }), _react.DOM.div({ className: "editor-mount devtools-monospace", style: this.getInlineEditorStyles() - }), this.renderBreakpoints(), this.renderHitCounts(), Footer({ editor: this.editor, horizontal }), this.renderPreview()); + }), this.renderHighlightLines(), this.renderBreakpoints(), this.renderHitCounts(), Footer({ editor: this.editor, horizontal }), this.renderPreview()); } } @@ -24475,6 +24663,9 @@ return /******/ (function(modules) { // webpackBootstrap hitCount: _react.PropTypes.object, selectedLocation: _react.PropTypes.object, selectedSource: _reactImmutableProptypes2.default.map, + highlightLineRange: _react.PropTypes.func, + clearHighlightLineRange: _react.PropTypes.func, + highlightedLineRange: _react.PropTypes.object, sourceText: _reactImmutableProptypes2.default.map, searchOn: _react.PropTypes.bool, addBreakpoint: _react.PropTypes.func.isRequired, @@ -24497,7 +24688,9 @@ return /******/ (function(modules) { // webpackBootstrap caseSensitive: _react.PropTypes.bool.isRequired, regexMatch: _react.PropTypes.bool.isRequired, wholeWord: _react.PropTypes.bool.isRequired - }).isRequired + }).isRequired, + startPanelSize: _react.PropTypes.number, + endPanelSize: _react.PropTypes.number }; Editor.contextTypes = { @@ -24515,6 +24708,7 @@ return /******/ (function(modules) { // webpackBootstrap return { selectedLocation, selectedSource, + highlightedLineRange: (0, _selectors.getHighlightedLineRange)(state), searchOn: (0, _selectors.getFileSearchState)(state), sourceText: (0, _selectors.getSourceText)(state, sourceId), loadedObjects: (0, _selectors.getLoadedObjects)(state), @@ -24834,9 +25028,7 @@ return /******/ (function(modules) { // webpackBootstrap var _debounce2 = _interopRequireDefault(_debounce); - var _reactImmutableProptypes = __webpack_require__(150); - - var _reactImmutableProptypes2 = _interopRequireDefault(_reactImmutableProptypes); + var _devtoolsSourceEditor = __webpack_require__(994); var _SearchInput2 = __webpack_require__(377); @@ -24856,15 +25048,27 @@ return /******/ (function(modules) { // webpackBootstrap var ResultList = (0, _react.createFactory)(_ResultList3.default); + function formatSymbol(symbol) { + return { + id: `${symbol.name}:${symbol.location.start.line}`, + title: symbol.name, + subtitle: `:${symbol.location.start.line}`, + value: symbol.name, + location: symbol.location + }; + } + function getShortcuts() { - var searchAgainKey = L10N.getStr("sourceSearch.search.again.key"); - var fnSearchKey = L10N.getStr("symbolSearch.search.key"); + var searchAgainKey = L10N.getStr("sourceSearch.search.again.key2"); + var searchAgainPrevKey = L10N.getStr("sourceSearch.search.againPrev.key2"); + var fnSearchKey = L10N.getStr("symbolSearch.search.key2"); + var searchKey = L10N.getStr("sourceSearch.search.key2"); return { - shiftSearchAgainShortcut: `CmdOrCtrl+Shift+${searchAgainKey}`, - searchAgainShortcut: `CmdOrCtrl+${searchAgainKey}`, - symbolSearchShortcut: `CmdOrCtrl+Shift+${fnSearchKey}`, - searchShortcut: `CmdOrCtrl+${L10N.getStr("sourceSearch.search.key")}` + shiftSearchAgainShortcut: searchAgainPrevKey, + searchAgainShortcut: searchAgainKey, + symbolSearchShortcut: fnSearchKey, + searchShortcut: searchKey }; } @@ -25010,6 +25214,7 @@ return /******/ (function(modules) { // webpackBootstrap this.props.toggleFileSearch(false); this.props.toggleSymbolSearch(false); this.props.setSelectedSymbolType("functions"); + this.props.clearHighlightLineRange(); e.stopPropagation(); e.preventDefault(); } @@ -25119,8 +25324,16 @@ return /******/ (function(modules) { // webpackBootstrap return; } - var symbolDeclarations = yield (0, _parser.getSymbols)(sourceText.toJS()); - var symbolSearchResults = (0, _fuzzaldrinPlus.filter)(symbolDeclarations[selectedSymbolType], query, { key: "value" }); + var _ref2 = yield (0, _parser.getSymbols)(sourceText.toJS()), + functions = _ref2.functions, + variables = _ref2.variables; + + var formattedSymbolDeclaration = { + variables: variables.map(formatSymbol), + functions: functions.map(formatSymbol) + }; + + var symbolSearchResults = (0, _fuzzaldrinPlus.filter)(formattedSymbolDeclaration[selectedSymbolType], query, { key: "value" }); updateSearchResults({ count: symbolSearchResults.length }); return _this.setState({ symbolSearchResults }); @@ -25273,13 +25486,24 @@ return /******/ (function(modules) { // webpackBootstrap onSelectResultItem(item) { var _props8 = this.props, selectSource = _props8.selectSource, - selectedSource = _props8.selectedSource; + selectedSource = _props8.selectedSource, + selectedSymbolType = _props8.selectedSymbolType, + highlightLineRange = _props8.highlightLineRange; - if (selectedSource) { + + if (selectedSource && selectedSymbolType !== "functions") { selectSource(selectedSource.get("id"), { line: item.location.start.line }); } + + if (selectedSource && selectedSymbolType === "functions") { + highlightLineRange({ + start: item.location.start.line, + end: item.location.end.line, + sourceId: selectedSource.get("id") + }); + } } onChange(e) { @@ -25468,31 +25692,7 @@ return /******/ (function(modules) { // webpackBootstrap } } - SearchBar.propTypes = { - editor: _react.PropTypes.object, - sourceText: _reactImmutableProptypes2.default.map, - selectSource: _react.PropTypes.func.isRequired, - selectedSource: _reactImmutableProptypes2.default.map, - searchOn: _react.PropTypes.bool, - toggleFileSearch: _react.PropTypes.func.isRequired, - searchResults: _react.PropTypes.object.isRequired, - modifiers: _reactImmutableProptypes2.default.recordOf({ - caseSensitive: _react.PropTypes.bool.isRequired, - regexMatch: _react.PropTypes.bool.isRequired, - wholeWord: _react.PropTypes.bool.isRequired - }).isRequired, - toggleFileSearchModifier: _react.PropTypes.func.isRequired, - symbolSearchOn: _react.PropTypes.bool.isRequired, - selectedSymbolType: _react.PropTypes.string, - toggleSymbolSearch: _react.PropTypes.func.isRequired, - setSelectedSymbolType: _react.PropTypes.func.isRequired, - query: _react.PropTypes.string.isRequired, - setFileSearchQuery: _react.PropTypes.func.isRequired, - updateSearchResults: _react.PropTypes.func.isRequired - }; - SearchBar.displayName = "SearchBar"; - SearchBar.contextTypes = { shortcuts: _react.PropTypes.object }; @@ -25630,7 +25830,38 @@ return /******/ (function(modules) { // webpackBootstrap /* 553 */, /* 554 */, /* 555 */, -/* 556 */, +/* 556 */ +/***/ function(module, exports) { + + /** + * A specialized version of `_.reduce` for arrays without support for + * iteratee shorthands. + * + * @private + * @param {Array} [array] The array to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @param {*} [accumulator] The initial value. + * @param {boolean} [initAccum] Specify using the first element of `array` as + * the initial value. + * @returns {*} Returns the accumulated value. + */ + function arrayReduce(array, iteratee, accumulator, initAccum) { + var index = -1, + length = array == null ? 0 : array.length; + + if (initAccum && length) { + accumulator = array[++index]; + } + while (++index < length) { + accumulator = iteratee(accumulator, array[index], index, array); + } + return accumulator; + } + + module.exports = arrayReduce; + + +/***/ }, /* 557 */, /* 558 */, /* 559 */, @@ -26262,8 +26493,11 @@ return /******/ (function(modules) { // webpackBootstrap var _props = this.props, loadObjectProperties = _props.loadObjectProperties, loadedObjects = _props.loadedObjects, - value = _props.value; + value = _props.value, + popoverTarget = _props.popoverTarget; + + popoverTarget.classList.add("selected-token"); if (!value || !value.type == "object") { return; @@ -26274,6 +26508,12 @@ return /******/ (function(modules) { // webpackBootstrap } } + componentWillUnmount() { + var popoverTarget = this.props.popoverTarget; + + popoverTarget.classList.remove("selected-token"); + } + getChildren(root, getObjectProperties) { var actors = {}; @@ -26323,8 +26563,7 @@ return /******/ (function(modules) { // webpackBootstrap getObjectProperties, autoExpandDepth: 0, onDoubleClick: () => {}, - loadObjectProperties, - getActors: () => ({}) + loadObjectProperties }); } @@ -26391,10 +26630,18 @@ return /******/ (function(modules) { // webpackBootstrap "use strict"; - var get = __webpack_require__(67); + Object.defineProperty(exports, "__esModule", { + value: true + }); + exports.getPromiseProperties = exports.isPromise = exports.createNode = exports.getChildren = exports.makeNodesForProperties = exports.sortProperties = exports.isDefault = exports.nodeIsFunction = exports.nodeIsObject = exports.nodeIsPrimitive = exports.nodeHasProperties = exports.nodeIsMissingArguments = exports.nodeIsOptimizedOut = exports.nodeHasChildren = undefined; + + var _get = __webpack_require__(67); - var _require = __webpack_require__(924), - maybeEscapePropertyName = _require.maybeEscapePropertyName; + var _get2 = _interopRequireDefault(_get); + + var _devtoolsReps = __webpack_require__(924); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } var WINDOW_PROPERTIES = {}; @@ -26403,7 +26650,7 @@ return /******/ (function(modules) { // webpackBootstrap } function getValue(item) { - return get(item, "contents.value", undefined); + return (0, _get2.default)(item, "contents.value", undefined); } function isBucket(item) { @@ -26507,21 +26754,24 @@ return /******/ (function(modules) { // webpackBootstrap var userProps = props.filter(name => !isDefault({ name })); var defaultProps = props.filter(name => isDefault({ name })); - var nodes = userProps.map(name => createNode(maybeEscapePropertyName(name), `${parentPath}/${name}`, ownProperties[name])); + var nodes = makeNodesForOwnProps(userProps, parentPath, ownProperties); if (defaultProps.length > 0) { - var defaultNodes = defaultProps.map((name, index) => createNode(maybeEscapePropertyName(name), `${parentPath}/bucket${index}/${name}`, ownProperties[name])); - nodes.push(createNode("[default properties]", `${parentPath}/default`, defaultNodes)); + var defaultNodes = defaultProps.map((name, index) => createNode((0, _devtoolsReps.maybeEscapePropertyName)(name), `${parentPath}/bucket${index}/${name}`, ownProperties[name])); + nodes.push(createNode("[default properties]", `${parentPath}/##-default`, defaultNodes)); } return nodes; } - /* + function makeNodesForOwnProps(properties, parentPath, ownProperties) { + return properties.map(name => createNode((0, _devtoolsReps.maybeEscapePropertyName)(name), `${parentPath}/${name}`, ownProperties[name])); + } + /* * Ignore non-concrete values like getters and setters * for now by making sure we have a value. */ - function makeNodesForProperties(objProps, parentPath) { + function makeNodesForProperties(objProps, parent) { var _ref = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}, _ref$bucketSize = _ref.bucketSize, bucketSize = _ref$bucketSize === undefined ? 100 : _ref$bucketSize; @@ -26530,7 +26780,8 @@ return /******/ (function(modules) { // webpackBootstrap prototype = objProps.prototype, ownSymbols = objProps.ownSymbols; - + var parentPath = parent.path; + var parentValue = parent.contents.value; var properties = sortProperties(Object.keys(ownProperties)).filter(name => ownProperties[name].hasOwnProperty("value")); var numProperties = properties.length; @@ -26538,8 +26789,10 @@ return /******/ (function(modules) { // webpackBootstrap var nodes = []; if (nodeIsArray(prototype) && numProperties > bucketSize) { nodes = makeNumericalBuckets(properties, bucketSize, parentPath, ownProperties); - } else { + } else if (parentValue.class == "Window") { nodes = makeDefaultPropsBucket(properties, parentPath, ownProperties); + } else { + nodes = makeNodesForOwnProps(properties, parentPath, ownProperties); } for (var index in ownSymbols) { @@ -26590,10 +26843,6 @@ return /******/ (function(modules) { // webpackBootstrap // node would be a new instance every render. var key = item.path; if (actors && actors[key]) { - if (item.contents.value && item.contents.value.preview) { - actors[key] = updateActor(item, actors, key); - } - return actors[key]; } @@ -26611,7 +26860,7 @@ return /******/ (function(modules) { // webpackBootstrap return []; } - var children = makeNodesForProperties(loadedProps, item.path); + var children = makeNodesForProperties(loadedProps, item); if (isPromise(item)) { children.unshift(getPromiseProperties(item)); } @@ -26619,42 +26868,20 @@ return /******/ (function(modules) { // webpackBootstrap return children; } - function updateActor(item, actors, key) { - var properties = item.contents.value.preview.ownProperties; - - var _loop2 = function (pKey) { - if (properties.hasOwnProperty(pKey)) { - var cacheObject = actors[key].filter(a => a.name == pKey)[0]; - var cacheObjectIndex = actors[key].findIndex(a => a.name == pKey); - // Assign new values to the cache actor if it goes stale - if (cacheObject && cacheObject.contents.value != properties[pKey].value) { - actors[key][cacheObjectIndex].contents = properties[pKey]; - } - } - }; - - for (var pKey in properties) { - _loop2(pKey); - } - return actors[key]; - } - - module.exports = { - nodeHasChildren, - nodeIsOptimizedOut, - nodeIsMissingArguments, - nodeHasProperties, - nodeIsPrimitive, - nodeIsObject, - nodeIsFunction, - isDefault, - sortProperties, - makeNodesForProperties, - getChildren, - createNode, - isPromise, - getPromiseProperties - }; + exports.nodeHasChildren = nodeHasChildren; + exports.nodeIsOptimizedOut = nodeIsOptimizedOut; + exports.nodeIsMissingArguments = nodeIsMissingArguments; + exports.nodeHasProperties = nodeHasProperties; + exports.nodeIsPrimitive = nodeIsPrimitive; + exports.nodeIsObject = nodeIsObject; + exports.nodeIsFunction = nodeIsFunction; + exports.isDefault = isDefault; + exports.sortProperties = sortProperties; + exports.makeNodesForProperties = makeNodesForProperties; + exports.getChildren = getChildren; + exports.createNode = createNode; + exports.isPromise = isPromise; + exports.getPromiseProperties = getPromiseProperties; /***/ }, /* 659 */, @@ -26734,6 +26961,8 @@ return /******/ (function(modules) { // webpackBootstrap var _ManagedTree3 = _interopRequireDefault(_ManagedTree2); + __webpack_require__(1009); + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } var ManagedTree = (0, _react.createFactory)(_ManagedTree3.default); @@ -26770,26 +26999,13 @@ return /******/ (function(modules) { // webpackBootstrap constructor() { super(); - this.actors = null; + this.actors = {}; var self = this; self.getChildren = this.getChildren.bind(this); self.renderItem = this.renderItem.bind(this); } - componentWillMount() { - // Cache of dynamically built nodes. We shouldn't need to clear - // this out ever, since we don't ever "switch out" the object - // being inspected. - this.actors = this.props.getActors(); - } - - componentWillUnmount() { - if (this.props.setActors) { - this.props.setActors(this.actors); - } - } - getChildren(item) { var getObjectProperties = this.props.getObjectProperties; var actors = this.actors; @@ -26807,13 +27023,14 @@ return /******/ (function(modules) { // webpackBootstrap var objectValue = void 0; var label = item.name; + var unavailable = (0, _objectInspector.nodeIsPrimitive)(item) && item.contents.value.hasOwnProperty("unavailable"); if ((0, _objectInspector.nodeIsOptimizedOut)(item)) { objectValue = _react.DOM.span({ className: "unavailable" }, "(optimized away)"); - } else if ((0, _objectInspector.nodeIsMissingArguments)(item)) { + } else if ((0, _objectInspector.nodeIsMissingArguments)(item) || unavailable) { objectValue = _react.DOM.span({ className: "unavailable" }, "(unavailable)"); } else if ((0, _objectInspector.nodeIsFunction)(item)) { objectValue = null; - label = (0, _previewFunction2.default)(item); + label = (0, _previewFunction2.default)({ name: label, parameterNames: [] }); } else if ((0, _objectInspector.nodeHasProperties)(item) || (0, _objectInspector.nodeIsPrimitive)(item)) { var object = item.contents.value; objectValue = (0, _Rep2.default)({ object, mode: _devtoolsReps.MODE.TINY }); @@ -26844,16 +27061,7 @@ return /******/ (function(modules) { // webpackBootstrap }) }), _react.DOM.span({ className: "object-label", - dir: "ltr", - onClick: event => { - event.stopPropagation(); - this.props.onLabelClick(item, { - depth, - focused, - expanded, - setExpanded - }); - } + dir: "ltr" }, label), _react.DOM.span({ className: "object-delimiter" }, objectValue ? ": " : ""), _react.DOM.span({ className: "object-value" }, objectValue || "")); } @@ -26862,9 +27070,7 @@ return /******/ (function(modules) { // webpackBootstrap name = _props.name, desc = _props.desc, loadObjectProperties = _props.loadObjectProperties, - autoExpandDepth = _props.autoExpandDepth, - getExpanded = _props.getExpanded, - setExpanded = _props.setExpanded; + autoExpandDepth = _props.autoExpandDepth; var roots = this.props.roots; @@ -26887,8 +27093,6 @@ return /******/ (function(modules) { // webpackBootstrap loadObjectProperties(item.contents.value); } }, - getExpanded, - setExpanded, renderItem: this.renderItem }); } @@ -26903,19 +27107,12 @@ return /******/ (function(modules) { // webpackBootstrap roots: _react.PropTypes.array, getObjectProperties: _react.PropTypes.func.isRequired, loadObjectProperties: _react.PropTypes.func.isRequired, - onLabelClick: _react.PropTypes.func.isRequired, - onDoubleClick: _react.PropTypes.func.isRequired, - getExpanded: _react.PropTypes.func, - setExpanded: _react.PropTypes.func, - getActors: _react.PropTypes.func.isRequired, - setActors: _react.PropTypes.func + onDoubleClick: _react.PropTypes.func.isRequired }; ObjectInspector.defaultProps = { - onLabelClick: () => {}, onDoubleClick: () => {}, - autoExpandDepth: 1, - getActors: () => ({}) + autoExpandDepth: 1 }; exports.default = ObjectInspector; @@ -26967,10 +27164,16 @@ return /******/ (function(modules) { // webpackBootstrap var _classnames2 = _interopRequireDefault(_classnames); + var _BracketArrow2 = __webpack_require__(1029); + + var _BracketArrow3 = _interopRequireDefault(_BracketArrow2); + __webpack_require__(699); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + var BracketArrow = (0, _react.createFactory)(_BracketArrow3.default); + class Popover extends _react.Component { constructor() { super(); @@ -26986,60 +27189,87 @@ return /******/ (function(modules) { // webpackBootstrap var _ref = type == "popover" ? this.getPopoverCoords() : this.getTooltipCoords(), left = _ref.left, top = _ref.top, - dir = _ref.dir; + orientation = _ref.orientation, + targetMid = _ref.targetMid; // eslint-disable-next-line react/no-did-mount-set-state - this.setState({ left, top, dir }); + this.setState({ left, top, orientation, targetMid }); } - getPopoverCoords() { - var el = _reactDom2.default.findDOMNode(this); + calculateLeft(target, editor, popover) { + var leftOffset = target.width / 2 - popover.width / 5; + var estimatedLeft = target.left + leftOffset; + var estimatedRight = estimatedLeft + popover.width; + var isOverflowingRight = estimatedRight > editor.right; + if (isOverflowingRight) { + var adjustedLeft = editor.right - popover.width; + return adjustedLeft; + } + return estimatedLeft; + } - var _el$getBoundingClient = el.getBoundingClientRect(), - width = _el$getBoundingClient.width, - height = _el$getBoundingClient.height; + calculateVerticalOrientation(target, editor, popover) { + var estimatedBottom = target.bottom + popover.height; - var _props$target$getBoun = this.props.target.getBoundingClientRect(), - targetLeft = _props$target$getBoun.left, - targetWidth = _props$target$getBoun.width, - targetBottom = _props$target$getBoun.bottom, - targetTop = _props$target$getBoun.top; + return estimatedBottom > editor.bottom ? "up" : "down"; + } - // width division corresponds to calc in Popover.css + getPopoverCoords() { + var popover = _reactDom2.default.findDOMNode(this); + var popoverRect = popover.getBoundingClientRect(); + + var editor = document.querySelector(".editor-wrapper"); + var editorRect = editor.getBoundingClientRect(); + var targetRect = this.props.target.getBoundingClientRect(); - var left = targetLeft + targetWidth / 2 - width / 5; - var dir = targetBottom + height > window.innerHeight ? "up" : "down"; - var top = dir == "down" ? targetBottom : targetTop - height; + var popoverLeft = this.calculateLeft(targetRect, editorRect, popoverRect); + var orientation = this.calculateVerticalOrientation(targetRect, editorRect, popoverRect); + var top = orientation == "down" ? targetRect.bottom : targetRect.top - popoverRect.height; - return { left, top, dir }; + var targetMid = targetRect.left - popoverLeft + targetRect.width / 2 - 8; + + return { left: popoverLeft, top, orientation, targetMid }; } getTooltipCoords() { var el = _reactDom2.default.findDOMNode(this); - var _el$getBoundingClient2 = el.getBoundingClientRect(), - height = _el$getBoundingClient2.height; + var _el$getBoundingClient = el.getBoundingClientRect(), + height = _el$getBoundingClient.height; - var _props$target$getBoun2 = this.props.target.getBoundingClientRect(), - targetLeft = _props$target$getBoun2.left, - targetWidth = _props$target$getBoun2.width, - targetTop = _props$target$getBoun2.top; + var _props$target$getBoun = this.props.target.getBoundingClientRect(), + targetLeft = _props$target$getBoun.left, + targetWidth = _props$target$getBoun.width, + targetTop = _props$target$getBoun.top; var left = targetLeft + targetWidth / 4 - 10; var top = targetTop - height; - return { left, top, dir: "up" }; + return { left, top, orientation: "up", targetMid: 0 }; } getChildren() { var children = this.props.children; - var dir = this.state.dir; + var orientation = this.state.orientation; var gap = _react.DOM.div({ className: "gap", key: "gap" }); - return dir === "up" ? [children, gap] : [gap, children]; + return orientation === "up" ? [children, gap] : [gap, children]; + } + + getPopoverArrow(orientation, left) { + var arrowOrientation = orientation === "up" ? "down" : "up"; + + var arrowProp = arrowOrientation === "up" ? "top" : "bottom"; + var arrowPropValue = arrowOrientation === "up" ? -8 : 6; + + return new BracketArrow({ + orientation: arrowOrientation, + left, + [arrowProp]: arrowPropValue + }); } renderPopover() { @@ -27047,14 +27277,17 @@ return /******/ (function(modules) { // webpackBootstrap var _state = this.state, top = _state.top, left = _state.left, - dir = _state.dir; + orientation = _state.orientation, + targetMid = _state.targetMid; + + var arrow = this.getPopoverArrow(orientation, targetMid); return _react.DOM.div({ - className: (0, _classnames2.default)("popover", { up: dir === "up" }), + className: (0, _classnames2.default)("popover", { up: orientation === "up" }), onMouseLeave, style: { top, left } - }, this.getChildren()); + }, arrow, this.getChildren()); } renderTooltip() { @@ -27130,20 +27363,24 @@ return /******/ (function(modules) { // webpackBootstrap var _flatten2 = _interopRequireDefault(_flatten); - __webpack_require__(1002); + __webpack_require__(1005); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } } - function renderFunctionName(value) { - var name = value.userDisplayName || value.displayName || value.name || ""; + function getFunctionName(func) { + return func.userDisplayName || func.displayName || func.name || func.value || ""; + } + + function renderFunctionName(func) { + var name = getFunctionName(func); return _react.DOM.span({ className: "function-name" }, name); } - function renderParams(value) { - var _value$parameterNames = value.parameterNames, - parameterNames = _value$parameterNames === undefined ? [] : _value$parameterNames; + function renderParams(func) { + var _func$parameterNames = func.parameterNames, + parameterNames = _func$parameterNames === undefined ? [] : _func$parameterNames; var params = parameterNames.filter(i => i).map(param => _react.DOM.span({ className: "param" }, param)); @@ -27156,8 +27393,8 @@ return /******/ (function(modules) { // webpackBootstrap return _react.DOM.span({ className: "paren" }, paren); } - function previewFunction(value) { - return _react.DOM.span.apply(_react.DOM, [{ className: "function-signature" }, renderFunctionName(value), renderParen("(")].concat(_toConsumableArray(renderParams(value)), [renderParen(")")])); + function previewFunction(func) { + return _react.DOM.span.apply(_react.DOM, [{ className: "function-signature" }, renderFunctionName(func), renderParen("(")].concat(_toConsumableArray(renderParams(func)), [renderParen(")")])); } exports.default = previewFunction; @@ -27590,11 +27827,6 @@ return /******/ (function(modules) { // webpackBootstrap } } - Breakpoint.propTypes = { - breakpoint: _react.PropTypes.object.isRequired, - editor: _react.PropTypes.object.isRequired - }; - Breakpoint.displayName = "Breakpoint"; exports.default = Breakpoint; @@ -27729,7 +27961,7 @@ return /******/ (function(modules) { // webpackBootstrap var _devtoolsSplitter2 = _interopRequireDefault(_devtoolsSplitter); - var _Frames2 = __webpack_require__(1005); + var _Frames2 = __webpack_require__(1012); var _Frames3 = _interopRequireDefault(_Frames2); @@ -27959,10 +28191,6 @@ return /******/ (function(modules) { // webpackBootstrap var _redux = __webpack_require__(3); - var _reactImmutableProptypes = __webpack_require__(150); - - var _reactImmutableProptypes2 = _interopRequireDefault(_reactImmutableProptypes); - var _actions = __webpack_require__(244); var _actions2 = _interopRequireDefault(_actions); @@ -27996,7 +28224,7 @@ return /******/ (function(modules) { // webpackBootstrap if (value.exception) { return { - path: expression.from, + path: value.from, value: value.exception }; } @@ -28113,6 +28341,10 @@ return /******/ (function(modules) { // webpackBootstrap value = _getValue.value, path = _getValue.path; + if (value.class == "Error") { + value = { unavailable: true }; + } + var root = { name: expression.input, path, @@ -28127,8 +28359,7 @@ return /******/ (function(modules) { // webpackBootstrap getObjectProperties: id => loadedObjects.get(id), autoExpandDepth: 0, onDoubleClick: (item, options) => this.editExpression(expression, options), - loadObjectProperties, - getActors: () => ({}) + loadObjectProperties }), CloseButton({ handleClick: e => this.deleteExpression(e, expression) })); } @@ -28171,16 +28402,6 @@ return /******/ (function(modules) { // webpackBootstrap } } - Expressions.propTypes = { - expressions: _reactImmutableProptypes2.default.list.isRequired, - addExpression: _react.PropTypes.func.isRequired, - evaluateExpressions: _react.PropTypes.func.isRequired, - updateExpression: _react.PropTypes.func.isRequired, - deleteExpression: _react.PropTypes.func.isRequired, - loadObjectProperties: _react.PropTypes.func, - loadedObjects: _reactImmutableProptypes2.default.map.isRequired - }; - Expressions.displayName = "Expressions"; exports.default = (0, _reactRedux.connect)(state => ({ @@ -28307,7 +28528,7 @@ return /******/ (function(modules) { // webpackBootstrap var _reactRedux = __webpack_require__(151); - var _reselect = __webpack_require__(992); + var _reselect = __webpack_require__(993); var _redux = __webpack_require__(3); @@ -28351,10 +28572,11 @@ return /******/ (function(modules) { // webpackBootstrap } - function renderSourceLocation(source, line) { + function renderSourceLocation(source, line, column) { var url = source.get("url") ? (0, _path.basename)(source.get("url")) : null; + var bpLocation = line + (column ? `:${column}` : ""); // const line = url !== "" ? `: ${line}` : ""; - return url ? _react.DOM.div({ className: "location" }, `${(0, _utils.endTruncateStr)(url, 30)}: ${line}`) : null; + return url ? _react.DOM.div({ className: "location" }, `${(0, _utils.endTruncateStr)(url, 30)}: ${bpLocation}`) : null; } class Breakpoints extends _react.PureComponent { @@ -28391,6 +28613,7 @@ return /******/ (function(modules) { // webpackBootstrap var snippet = breakpoint.text || ""; var locationId = breakpoint.locationId; var line = breakpoint.location.line; + var column = breakpoint.location.column; var isCurrentlyPaused = breakpoint.isCurrentlyPaused; var isDisabled = breakpoint.disabled; var isConditional = !!breakpoint.condition; @@ -28412,7 +28635,7 @@ return /******/ (function(modules) { // webpackBootstrap // Prevent clicking on the checkbox from triggering the onClick of // the surrounding div onClick: ev => ev.stopPropagation() - }), _react.DOM.div({ className: "breakpoint-label", title: breakpoint.text }, _react.DOM.div({}, renderSourceLocation(breakpoint.location.source, line))), _react.DOM.div({ className: "breakpoint-snippet" }, snippet), (0, _Close2.default)({ + }), _react.DOM.div({ className: "breakpoint-label", title: breakpoint.text }, _react.DOM.div({}, renderSourceLocation(breakpoint.location.source, line, column))), _react.DOM.div({ className: "breakpoint-snippet" }, snippet), (0, _Close2.default)({ handleClick: ev => this.removeBreakpoint(ev, breakpoint), tooltip: L10N.getStr("breakpoints.removeBreakpointTooltip") })); @@ -28741,9 +28964,6 @@ return /******/ (function(modules) { // webpackBootstrap return _react.DOM.div({ className: "pane-info" }, text); } - var expandedCache = new Set(); - var actorsCache = []; - class Scopes extends _react.PureComponent { constructor(props) { @@ -28789,21 +29009,7 @@ return /******/ (function(modules) { // webpackBootstrap scopeInspector = ObjectInspector({ roots: scopes, getObjectProperties: id => loadedObjects.get(id), - loadObjectProperties: loadObjectProperties, - setExpanded: expanded => { - expandedCache = expanded; - }, - getExpanded: () => expandedCache, - setActors: actors => { - actorsCache = actors; - }, - getActors: () => actorsCache, - onLabelClick: (item, _ref) => { - var expanded = _ref.expanded, - setExpanded = _ref.setExpanded; - - setExpanded(item, !expanded); - } + loadObjectProperties: loadObjectProperties }); } @@ -29051,7 +29257,6 @@ return /******/ (function(modules) { // webpackBootstrap function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } var dom = _react2.default.DOM, - PropTypes = _react2.default.PropTypes, Component = _react2.default.Component; @@ -29117,15 +29322,6 @@ return /******/ (function(modules) { // webpackBootstrap } } - EventListeners.propTypes = { - listeners: PropTypes.array.isRequired, - selectSource: PropTypes.func.isRequired, - addBreakpoint: PropTypes.func.isRequired, - enableBreakpoint: PropTypes.func.isRequired, - disableBreakpoint: PropTypes.func.isRequired, - removeBreakpoint: PropTypes.func.isRequired - }; - EventListeners.displayName = "EventListeners"; exports.default = (0, _reactRedux.connect)(state => { @@ -29263,10 +29459,6 @@ return /******/ (function(modules) { // webpackBootstrap var _redux = __webpack_require__(3); - var _reactImmutableProptypes = __webpack_require__(150); - - var _reactImmutableProptypes2 = _interopRequireDefault(_reactImmutableProptypes); - var _selectors = __webpack_require__(242); var _Svg = __webpack_require__(344); @@ -29365,6 +29557,7 @@ return /******/ (function(modules) { // webpackBootstrap } class CommandBar extends _react.Component { + componentWillUnmount() { var shortcuts = this.context.shortcuts; COMMANDS.forEach(action => shortcuts.off(getKey(action))); @@ -29451,23 +29644,8 @@ return /******/ (function(modules) { // webpackBootstrap } } - CommandBar.propTypes = { - sources: _react.PropTypes.object, - selectedSource: _react.PropTypes.object, - resume: _react.PropTypes.func, - stepIn: _react.PropTypes.func, - stepOut: _react.PropTypes.func, - stepOver: _react.PropTypes.func, - breakOnNext: _react.PropTypes.func, - pause: _reactImmutableProptypes2.default.map, - pauseOnExceptions: _react.PropTypes.func, - shouldPauseOnExceptions: _react.PropTypes.bool, - shouldIgnoreCaughtExceptions: _react.PropTypes.bool, - isWaitingOnBreak: _react.PropTypes.bool - }; - CommandBar.contextTypes = { - shortcuts: _react.PropTypes.object + shortcuts: Object }; CommandBar.displayName = "CommandBar"; @@ -29531,6 +29709,7 @@ return /******/ (function(modules) { // webpackBootstrap class WelcomeBox extends _react.Component { + renderToggleButton() { if (this.props.horizontal) { return; @@ -29545,17 +29724,11 @@ return /******/ (function(modules) { // webpackBootstrap } render() { - var searchLabel = L10N.getFormatStr("welcome.search", (0, _text.formatKeyShortcut)(`CmdOrCtrl+${L10N.getStr("sources.search.key")}`)); + var searchLabel = L10N.getFormatStr("welcome.search", (0, _text.formatKeyShortcut)(L10N.getStr("sources.search.key2"))); return _react.DOM.div({ className: "welcomebox" }, searchLabel, this.renderToggleButton()); } } - WelcomeBox.propTypes = { - horizontal: _react.PropTypes.bool, - togglePaneCollapse: _react.PropTypes.func, - endPanelCollapsed: _react.PropTypes.bool - }; - WelcomeBox.displayName = "WelcomeBox"; exports.default = (0, _reactRedux.connect)(state => ({ @@ -29912,7 +30085,7 @@ return /******/ (function(modules) { // webpackBootstrap } renderNewButton() { - var newTabTooltip = L10N.getFormatStr("sourceTabs.newTabButtonTooltip", (0, _text.formatKeyShortcut)(`CmdOrCtrl+${L10N.getStr("sources.search.key")}`)); + var newTabTooltip = L10N.getFormatStr("sourceTabs.newTabButtonTooltip", (0, _text.formatKeyShortcut)(L10N.getStr("sources.search.key2"))); return _react.DOM.div({ className: "new-tab-btn", onClick: () => this.props.toggleProjectSearch(), @@ -30139,8 +30312,10 @@ return /******/ (function(modules) { // webpackBootstrap "use strict"; - var _require = __webpack_require__(900), - WorkerDispatcher = _require.workerUtils.WorkerDispatcher; + var _devtoolsUtils = __webpack_require__(900); + + var WorkerDispatcher = _devtoolsUtils.workerUtils.WorkerDispatcher; + var dispatcher = new WorkerDispatcher(); @@ -30849,10 +31024,10 @@ return /******/ (function(modules) { // webpackBootstrap return {}; } - yield chrome.connectNode(tab.tab); - chrome.initPage({ clientType }); + var tabConnection = yield chrome.connectNode(tab.tab); + chrome.initPage({ clientType, tabConnection }); - return { tabs, tab, clientType, client: chrome }; + return { tabs, tab, clientType, client: chrome, tabConnection }; }); return function startDebuggingNode(_x) { @@ -31421,6 +31596,7 @@ return /******/ (function(modules) { // webpackBootstrap function onNewBreakpoint(location, res) { var bpClient = res[1]; var actualLocation = res[0].actualLocation; + bpClients[bpClient.actor] = bpClient; // Firefox only returns `actualLocation` if it actually changed, @@ -31428,6 +31604,7 @@ return /******/ (function(modules) { // webpackBootstrap // exists, otherwise use `location`. actualLocation = actualLocation ? { sourceId: actualLocation.source.actor, + sourceUrl: location.sourceUrl, line: actualLocation.line, column: actualLocation.column } : location; @@ -32144,6 +32321,7 @@ return /******/ (function(modules) { // webpackBootstrap function bootstrapStore(client, services) { var createStore = (0, _createStore2.default)({ log: (0, _devtoolsConfig.getValue)("logging.actions"), + timing: (0, _devtoolsConfig.getValue)("performance.actions"), makeThunkArgs: (args, state) => { return Object.assign({}, args, { client }, services); } @@ -32435,7 +32613,7 @@ return /******/ (function(modules) { // webpackBootstrap var contentType = sourceText ? sourceText.contentType : ""; var indent = 2; - (0, _assert2.default)(isJavaScript(source.url, contentType), "Can't prettify non-javascript files."); + (0, _assert2.default)((0, _source.isJavaScript)(source.url, contentType), "Can't prettify non-javascript files."); return yield _prettyPrint({ url, @@ -32449,6 +32627,10 @@ return /******/ (function(modules) { // webpackBootstrap }; })(); + var _devtoolsUtils = __webpack_require__(900); + + var _source = __webpack_require__(233); + var _assert = __webpack_require__(223); var _assert2 = _interopRequireDefault(_assert); @@ -32457,11 +32639,8 @@ return /******/ (function(modules) { // webpackBootstrap function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; } - var _require = __webpack_require__(900), - WorkerDispatcher = _require.workerUtils.WorkerDispatcher; + var WorkerDispatcher = _devtoolsUtils.workerUtils.WorkerDispatcher; - var _require2 = __webpack_require__(233), - isJavaScript = _require2.isJavaScript; var dispatcher = new WorkerDispatcher(); var _prettyPrint = dispatcher.task("prettyPrint"); @@ -32551,6 +32730,11 @@ return /******/ (function(modules) { // webpackBootstrap } if (variables.has(tokenText)) { + var variableKey = variables.get(tokenText); + if ((0, _get2.default)(variableKey, "contents.value.type") == "undefined") { + return null; + } + return variables.get(tokenText); } @@ -32987,15 +33171,15 @@ return /******/ (function(modules) { // webpackBootstrap componentWillUnmount() { var shortcuts = this.context.shortcuts; - var searchKeys = [L10N.getStr("sources.search.key"), L10N.getStr("symbolSearch.search.key")]; - searchKeys.forEach(key => shortcuts.off(`CmdOrCtrl+${key}`, this.toggle)); + var searchKeys = [L10N.getStr("sources.search.key2"), L10N.getStr("sources.search.key2")]; + searchKeys.forEach(key => shortcuts.off(key, this.toggle)); shortcuts.off("Escape", this.onEscape); } componentDidMount() { var shortcuts = this.context.shortcuts; - var searchKeys = [L10N.getStr("sources.search.key"), L10N.getStr("symbolSearch.search.key")]; - searchKeys.forEach(key => shortcuts.on(`CmdOrCtrl+${key}`, this.toggle)); + var searchKeys = [L10N.getStr("sources.search.key2"), L10N.getStr("sources.search.alt.key")]; + searchKeys.forEach(key => shortcuts.on(key, this.toggle)); shortcuts.on("Escape", this.onEscape); } @@ -33635,6 +33819,10 @@ return /******/ (function(modules) { // webpackBootstrap __webpack_require__(922); + var _previewFunction = __webpack_require__(701); + + var _previewFunction2 = _interopRequireDefault(_previewFunction); + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; } @@ -33654,6 +33842,7 @@ return /******/ (function(modules) { // webpackBootstrap } } + // TODO: move this logic out of the component and into a reducer setSymbolDeclarations(sourceText) { var _this = this; @@ -33668,8 +33857,22 @@ return /******/ (function(modules) { // webpackBootstrap })(); } + selectItem(location) { + var _props = this.props, + selectedSource = _props.selectedSource, + selectSource = _props.selectSource; + + var selectedSourceId = selectedSource.get("id"); + var startLine = location.start.line; + selectSource(selectedSourceId, { line: startLine }); + } + renderFunction(func) { - return _react.DOM.li({}, func.value); + return _react.DOM.li({ + key: func.id, + className: "outline-list__element", + onClick: () => this.selectItem(func.location) + }, (0, _previewFunction2.default)(func)); } renderFunctions() { @@ -33682,7 +33885,7 @@ return /******/ (function(modules) { // webpackBootstrap var functions = symbolDeclarations.functions; - return functions.filter(func => func.value != "anonymous").map(this.renderFunction); + return functions.filter(func => func.value != "anonymous").map(func => this.renderFunction(func)); } render() { @@ -33690,11 +33893,12 @@ return /******/ (function(modules) { // webpackBootstrap return null; } - return _react.DOM.div({ className: "outline" }, _react.DOM.ul({}, this.renderFunctions())); + return _react.DOM.div({ className: "outline" }, _react.DOM.ul({ className: "outline-list" }, this.renderFunctions())); } } Outline.propTypes = { + selectSource: _react.PropTypes.func.isRequired, selectedSource: _react.PropTypes.object }; @@ -33705,7 +33909,8 @@ return /******/ (function(modules) { // webpackBootstrap var sourceId = selectedSource ? selectedSource.get("id") : null; return { - sourceText: (0, _selectors.getSourceText)(state, sourceId) + sourceText: (0, _selectors.getSourceText)(state, sourceId), + selectedSource }; }, dispatch => (0, _redux.bindActionCreators)(_actions2.default, dispatch))(Outline); @@ -36988,7 +37193,7 @@ return /******/ (function(modules) { // webpackBootstrap /* 960 */ /***/ function(module, exports) { - module.exports = "# This Source Code Form is subject to the terms of the Mozilla Public\n# License, v. 2.0. If a copy of the MPL was not distributed with this\n# file, You can obtain one at http://mozilla.org/MPL/2.0/.\n\n# LOCALIZATION NOTE These strings are used inside the Debugger\n# which is available from the Web Developer sub-menu -> 'Debugger'.\n# The correct localization of this file might be to keep it in\n# English, or another language commonly spoken among web developers.\n# You want to make that choice consistent across the developer tools.\n# A good criteria is the language in which you'd find the best\n# documentation on web development on the web.\n\n# LOCALIZATION NOTE (collapsePanes): This is the tooltip for the button\n# that collapses the left and right panes in the debugger UI.\ncollapsePanes=Collapse panes\n\n# LOCALIZATION NOTE (copySourceUrl): This is the text that appears in the\n# context menu to copy the source URL of file open.\ncopySourceUrl=Copy Source Url\n\n# LOCALIZATION NOTE (copySourceUrl.accesskey): Access key to copy the source URL of a file from\n# the context menu.\ncopySourceUrl.accesskey=u\n\n# LOCALIZATION NOTE (copyStackTrace): This is the text that appears in the\n# context menu to copy the stack trace methods, file names and row number.\ncopyStackTrace=Copy Stack Trace\n\n# LOCALIZATION NOTE (copyStackTrace.accesskey): Access key to copy the stack trace data from\n# the context menu.\ncopyStackTrace.accesskey=c\n\n# LOCALIZATION NOTE (expandPanes): This is the tooltip for the button\n# that expands the left and right panes in the debugger UI.\nexpandPanes=Expand panes\n\n# LOCALIZATION NOTE (pauseButtonTooltip): The tooltip that is displayed for the pause\n# button when the debugger is in a running state.\npauseButtonTooltip=Pause %S\n\n# LOCALIZATION NOTE (pausePendingButtonTooltip): The tooltip that is displayed for\n# the pause button after it's been clicked but before the next JavaScript to run.\npausePendingButtonTooltip=Waiting for next execution\n\n# LOCALIZATION NOTE (resumeButtonTooltip): The label that is displayed on the pause\n# button when the debugger is in a paused state.\nresumeButtonTooltip=Resume %S\n\n# LOCALIZATION NOTE (stepOverTooltip): The label that is displayed on the\n# button that steps over a function call.\nstepOverTooltip=Step Over %S\n\n# LOCALIZATION NOTE (stepInTooltip): The label that is displayed on the\n# button that steps into a function call.\nstepInTooltip=Step In %S\n\n# LOCALIZATION NOTE (stepOutTooltip): The label that is displayed on the\n# button that steps out of a function call.\nstepOutTooltip=Step Out %S\n\n# LOCALIZATION NOTE (noWorkersText): The text to display in the workers list\n# when there are no workers.\nnoWorkersText=This page has no workers.\n\n# LOCALIZATION NOTE (noSourcesText): The text to display in the sources list\n# when there are no sources.\nnoSourcesText=This page has no sources.\n\n# LOCALIZATION NOTE (noEventListenersText): The text to display in the events tab\n# when there are no events.\nnoEventListenersText=No event listeners to display\n\n# LOCALIZATION NOTE (eventListenersHeader): The text to display in the events\n# header.\neventListenersHeader=Event Listeners\n\n# LOCALIZATION NOTE (noStackFramesText): The text to display in the call stack tab\n# when there are no stack frames.\nnoStackFramesText=No stack frames to display\n\n# LOCALIZATION NOTE (eventCheckboxTooltip): The tooltip text to display when\n# the user hovers over the checkbox used to toggle an event breakpoint.\neventCheckboxTooltip=Toggle breaking on this event\n\n# LOCALIZATION NOTE (eventOnSelector): The text to display in the events tab\n# for every event item, between the event type and event selector.\neventOnSelector=on\n\n# LOCALIZATION NOTE (eventInSource): The text to display in the events tab\n# for every event item, between the event selector and listener's owner source.\neventInSource=in\n\n# LOCALIZATION NOTE (eventNodes): The text to display in the events tab when\n# an event is listened on more than one target node.\neventNodes=%S nodes\n\n# LOCALIZATION NOTE (eventNative): The text to display in the events tab when\n# a listener is added from plugins, thus getting translated to native code.\neventNative=[native code]\n\n# LOCALIZATION NOTE (*Events): The text to display in the events tab for\n# each group of sub-level event entries.\nanimationEvents=Animation\naudioEvents=Audio\nbatteryEvents=Battery\nclipboardEvents=Clipboard\ncompositionEvents=Composition\ndeviceEvents=Device\ndisplayEvents=Display\ndragAndDropEvents=Drag and Drop\ngamepadEvents=Gamepad\nindexedDBEvents=IndexedDB\ninteractionEvents=Interaction\nkeyboardEvents=Keyboard\nmediaEvents=HTML5 Media\nmouseEvents=Mouse\nmutationEvents=Mutation\nnavigationEvents=Navigation\npointerLockEvents=Pointer Lock\nsensorEvents=Sensor\nstorageEvents=Storage\ntimeEvents=Time\ntouchEvents=Touch\notherEvents=Other\n\n# LOCALIZATION NOTE (blackboxCheckboxTooltip2): The tooltip text to display when\n# the user hovers over the checkbox used to toggle blackboxing its associated\n# source.\nblackboxCheckboxTooltip2=Toggle blackboxing\n\n# LOCALIZATION NOTE (sources.search.key): Key shortcut to open the search for\n# searching all the source files the debugger has seen.\nsources.search.key=P\n\n# LOCALIZATION NOTE (sources.noSourcesAvailable): Text shown when the debugger\n# does not have any sources.\nsources.noSourcesAvailable=This page has no sources\n\n# LOCALIZATION NOTE (sourceSearch.search.key): Key shortcut to open the search\n# for searching within a the currently opened files in the editor\nsourceSearch.search.key=F\n\n# LOCALIZATION NOTE (sourceSearch.search.placeholder): placeholder text in\n# the source search input bar\nsourceSearch.search.placeholder=Search in file…\n\n# LOCALIZATION NOTE (sourceSearch.search.again.key): Key shortcut to re-open\n# the search for re-searching the same search triggered from a sourceSearch\nsourceSearch.search.again.key=G\n\n# LOCALIZATION NOTE (sourceSearch.resultsSummary1): Shows a summary of\n# the number of matches for autocomplete\nsourceSearch.resultsSummary1=%d results\n\n# LOCALIZATION NOTE (noMatchingStringsText): The text to display in the\n# global search results when there are no matching strings after filtering.\nnoMatchingStringsText=No matches found\n\n# LOCALIZATION NOTE (emptySearchText): This is the text that appears in the\n# filter text box when it is empty and the scripts container is selected.\nemptySearchText=Search scripts (%S)\n\n# LOCALIZATION NOTE (emptyVariablesFilterText): This is the text that\n# appears in the filter text box for the variables view container.\nemptyVariablesFilterText=Filter variables\n\n# LOCALIZATION NOTE (emptyPropertiesFilterText): This is the text that\n# appears in the filter text box for the editor's variables view bubble.\nemptyPropertiesFilterText=Filter properties\n\n# LOCALIZATION NOTE (searchPanelFilter): This is the text that appears in the\n# filter panel popup for the filter scripts operation.\nsearchPanelFilter=Filter scripts (%S)\n\n# LOCALIZATION NOTE (searchPanelGlobal): This is the text that appears in the\n# filter panel popup for the global search operation.\nsearchPanelGlobal=Search in all files (%S)\n\n# LOCALIZATION NOTE (searchPanelFunction): This is the text that appears in the\n# filter panel popup for the function search operation.\nsearchPanelFunction=Search for function definition (%S)\n\n# LOCALIZATION NOTE (searchPanelToken): This is the text that appears in the\n# filter panel popup for the token search operation.\nsearchPanelToken=Find in this file (%S)\n\n# LOCALIZATION NOTE (searchPanelGoToLine): This is the text that appears in the\n# filter panel popup for the line search operation.\nsearchPanelGoToLine=Go to line (%S)\n\n# LOCALIZATION NOTE (searchPanelVariable): This is the text that appears in the\n# filter panel popup for the variables search operation.\nsearchPanelVariable=Filter variables (%S)\n\n# LOCALIZATION NOTE (breakpointMenuItem): The text for all the elements that\n# are displayed in the breakpoints menu item popup.\nbreakpointMenuItem.setConditional=Configure conditional breakpoint\nbreakpointMenuItem.enableSelf=Enable breakpoint\nbreakpointMenuItem.disableSelf=Disable breakpoint\nbreakpointMenuItem.deleteSelf=Remove breakpoint\nbreakpointMenuItem.enableOthers=Enable others\nbreakpointMenuItem.disableOthers=Disable others\nbreakpointMenuItem.deleteOthers=Remove others\nbreakpointMenuItem.enableAll=Enable all breakpoints\nbreakpointMenuItem.disableAll=Disable all breakpoints\nbreakpointMenuItem.deleteAll=Remove all breakpoints\n\n# LOCALIZATION NOTE (breakpoints.header): Breakpoints right sidebar pane header.\nbreakpoints.header=Breakpoints\n\n# LOCALIZATION NOTE (breakpoints.none): The text that appears when there are\n# no breakpoints present\nbreakpoints.none=No Breakpoints\n\n# LOCALIZATION NOTE (breakpoints.enable): The text that may appear as a tooltip\n# when hovering over the 'disable breakpoints' switch button in right sidebar\nbreakpoints.enable=Enable Breakpoints\n\n# LOCALIZATION NOTE (breakpoints.disable): The text that may appear as a tooltip\n# when hovering over the 'disable breakpoints' switch button in right sidebar\nbreakpoints.disable=Disable Breakpoints\n\n# LOCALIZATION NOTE (breakpoints.removeBreakpointTooltip): The tooltip that is displayed\n# for remove breakpoint button in right sidebar\nbreakpoints.removeBreakpointTooltip=Remove Breakpoint\n\n# LOCALIZATION NOTE (callStack.header): Call Stack right sidebar pane header.\ncallStack.header=Call Stack\n\n# LOCALIZATION NOTE (callStack.notPaused): Call Stack right sidebar pane\n# message when not paused.\ncallStack.notPaused=Not Paused\n\n# LOCALIZATION NOTE (callStack.collapse): Call Stack right sidebar pane\n# message to hide some of the frames that are shown.\ncallStack.collapse=Collapse Rows\n\n# LOCALIZATION NOTE (callStack.expand): Call Stack right sidebar pane\n# message to show more of the frames.\ncallStack.expand=Expand Rows\n\n# LOCALIZATION NOTE (editor.searchResults): Editor Search bar message\n# for the summarizing the selected search result. e.g. 5 of 10 results.\neditor.searchResults=%d of %d results\n\n# LOCALIZATION NOTE (sourceSearch.singleResult): Copy shown when there is one result.\neditor.singleResult=1 result\n\n# LOCALIZATION NOTE (editor.noResults): Editor Search bar message\n# for when no results found.\neditor.noResults=no results\n\n# LOCALIZATION NOTE (editor.searchResults.nextResults): Editor Search bar\n# tooltip for traversing to the Next Result\neditor.searchResults.nextResult=Next Result\n\n# LOCALIZATION NOTE (editor.searchResults.prevResults): Editor Search bar\n# tooltip for traversing to the Previous Result\neditor.searchResults.prevResult=Previous Result\n\n# LOCALIZATION NOTE (editor.searchTypeToggleTitle): Search bar title for\n# toggling search type buttons(function search, variable search)\neditor.searchTypeToggleTitle=Search for:\n\n# LOCALIZATION NOTE (editor.addBreakpoint): Editor gutter context menu item\n# for adding a breakpoint on a line.\neditor.addBreakpoint=Add Breakpoint\n\n# LOCALIZATION NOTE (editor.disableBreakpoint): Editor gutter context menu item\n# for disabling a breakpoint on a line.\neditor.disableBreakpoint=Disable Breakpoint\n\n# LOCALIZATION NOTE (editor.enableBreakpoint): Editor gutter context menu item\n# for enabling a breakpoint on a line.\neditor.enableBreakpoint=Enable Breakpoint\n\n# LOCALIZATION NOTE (editor.removeBreakpoint): Editor gutter context menu item\n# for removing a breakpoint on a line.\neditor.removeBreakpoint=Remove Breakpoint\n\n# LOCALIZATION NOTE (editor.editBreakpoint): Editor gutter context menu item\n# for setting a breakpoint condition on a line.\neditor.editBreakpoint=Edit Breakpoint\n\n# LOCALIZATION NOTE (editor.addConditionalBreakpoint): Editor gutter context\n# menu item for adding a breakpoint condition on a line.\neditor.addConditionalBreakpoint=Add Conditional Breakpoint\n\n# LOCALIZATION NOTE (editor.conditionalPanel.placeholder): Placeholder text for\n# input element inside ConditionalPanel component\neditor.conditionalPanel.placeholder=This breakpoint will pause when the expression is true\n\n# LOCALIZATION NOTE (editor.conditionalPanel.placeholder): Tooltip text for\n# close button inside ConditionalPanel component\neditor.conditionalPanel.close=Cancel edit breakpoint and close\n\n# LOCALIZATION NOTE (editor.jumpToMappedLocation1): Context menu item\n# for navigating to a source mapped location\neditor.jumpToMappedLocation1=Jump to %S location\n\n# LOCALIZATION NOTE (generated): Source Map term for a server source location\ngenerated=generated\n\n# LOCALIZATION NOTE (original): Source Map term for a debugger UI source location\noriginal=original\n\n# LOCALIZATION NOTE (expressions.placeholder): Placeholder text for expression\n# input element\nexpressions.placeholder=Add Watch Expression\n\n# LOCALIZATION NOTE (sourceTabs.closeTab): Editor source tab context menu item\n# for closing the selected tab below the mouse.\nsourceTabs.closeTab=Close tab\n\n# LOCALIZATION NOTE (sourceTabs.closeTab.accesskey): Access key to close the currently select\n# source tab from the editor context menu item.\nsourceTabs.closeTab.accesskey=c\n\n# LOCALIZATION NOTE (sourceTabs.closeOtherTabs): Editor source tab context menu item\n# for closing the other tabs.\nsourceTabs.closeOtherTabs=Close others\n\n# LOCALIZATION NOTE (sourceTabs.closeOtherTabs.accesskey): Access key to close other source tabs\n# from the editor context menu.\nsourceTabs.closeOtherTabs.accesskey=o\n\n# LOCALIZATION NOTE (sourceTabs.closeTabsToEnd): Editor source tab context menu item\n# for closing the tabs to the end (the right for LTR languages) of the selected tab.\nsourceTabs.closeTabsToEnd=Close tabs to the right\n\n# LOCALIZATION NOTE (sourceTabs.closeTabsToEnd.accesskey): Access key to close source tabs\n# after the selected tab from the editor context menu.\nsourceTabs.closeTabsToEnd.accesskey=e\n\n# LOCALIZATION NOTE (sourceTabs.closeAllTabs): Editor source tab context menu item\n# for closing all tabs.\nsourceTabs.closeAllTabs=Close all tabs\n\n# LOCALIZATION NOTE (sourceTabs.closeAllTabs.accesskey): Access key to close all tabs from the\n# editor context menu.\nsourceTabs.closeAllTabs.accesskey=a\n\n# LOCALIZATION NOTE (sourceTabs.revealInTree): Editor source tab context menu item\n# for revealing source in tree.\nsourceTabs.revealInTree=Reveal in Tree\n\n# LOCALIZATION NOTE (sourceTabs.revealInTree.accesskey): Access key to reveal a source in the\n# tree from the context menu.\nsourceTabs.revealInTree.accesskey=r\n\n# LOCALIZATION NOTE (sourceTabs.copyLink): Editor source tab context menu item\n# for copying a link address.\nsourceTabs.copyLink=Copy Link Address\n\n# LOCALIZATION NOTE (sourceTabs.copyLink.accesskey): Access key to copy a link addresss from the\n# editor context menu.\nsourceTabs.copyLink.accesskey=l\n\n# LOCALIZATION NOTE (sourceTabs.prettyPrint): Editor source tab context menu item\n# for pretty printing the source.\nsourceTabs.prettyPrint=Pretty Print Source\n\n# LOCALIZATION NOTE (sourceTabs.prettyPrint.accesskey): Access key to pretty print a source from\n# the editor context menu.\nsourceTabs.prettyPrint.accesskey=p\n\n# LOCALIZATION NOTE (sourceFooter.blackbox): Tooltip text associated\n# with the blackbox button\nsourceFooter.blackbox=Blackbox Source\n\n# LOCALIZATION NOTE (sourceFooter.unblackbox): Tooltip text associated\n# with the blackbox button\nsourceFooter.unblackbox=Unblackbox Source\n\n# LOCALIZATION NOTE (sourceFooter.blackbox.accesskey): Access key to blackbox\n# an associated source\nsourceFooter.blackbox.accesskey=b\n\n# LOCALIZATION NOTE (sourceFooter.blackboxed): Text associated\n# with a blackboxed source\nsourceFooter.blackboxed=Blackboxed Source\n\n# LOCALIZATION NOTE (sourceTabs.closeTabButtonTooltip): The tooltip that is displayed\n# for close tab button in source tabs.\nsourceTabs.closeTabButtonTooltip=Close tab\n\n# LOCALIZATION NOTE (sourceTabs.newTabButtonTooltip): The tooltip that is displayed for\n# new tab button in source tabs.\nsourceTabs.newTabButtonTooltip=Search for sources (%S)\n\n# LOCALIZATION NOTE (scopes.header): Scopes right sidebar pane header.\nscopes.header=Scopes\n\n# LOCALIZATION NOTE (scopes.notAvailable): Scopes right sidebar pane message\n# for when the debugger is paused, but there isn't pause data.\nscopes.notAvailable=Scopes Unavailable\n\n# LOCALIZATION NOTE (scopes.notPaused): Scopes right sidebar pane message\n# for when the debugger is not paused.\nscopes.notPaused=Not Paused\n\n# LOCALIZATION NOTE (scopes.block): Refers to a block of code in\n# the scopes pane when the debugger is paused.\nscopes.block=Block\n\n# LOCALIZATION NOTE (sources.header): Sources left sidebar header\nsources.header=Sources\n\n# LOCALIZATION NOTE (sources.search): Sources left sidebar prompt\n# e.g. Cmd+P to search. On a mac, we use the command unicode character.\n# On windows, it's ctrl.\nsources.search=%S to search\n\n# LOCALIZATION NOTE (watchExpressions.header): Watch Expressions right sidebar\n# pane header.\nwatchExpressions.header=Watch Expressions\n\n# LOCALIZATION NOTE (watchExpressions.refreshButton): Watch Expressions header\n# button for refreshing the expressions.\nwatchExpressions.refreshButton=Refresh\n\n# LOCALIZATION NOTE (welcome.search): The center pane welcome panel's\n# search prompt. e.g. cmd+p to search for files. On windows, it's ctrl, on\n# a mac we use the unicode character.\nwelcome.search=%S to search for sources\n\n# LOCALIZATION NOTE (sourceSearch.search): The center pane Source Search\n# prompt for searching for files.\nsourceSearch.search=Search Sources…\n\n# LOCALIZATION NOTE (sourceSearch.noResults): The center pane Source Search\n# message when the query did not match any of the sources.\nsourceSearch.noResults=No files matching %S found\n\n# LOCALIZATION NOTE (ignoreExceptions): The pause on exceptions button tooltip\n# when the debugger will not pause on exceptions.\nignoreExceptions=Ignore exceptions. Click to pause on uncaught exceptions\n\n# LOCALIZATION NOTE (pauseOnUncaughtExceptions): The pause on exceptions button\n# tooltip when the debugger will pause on uncaught exceptions.\npauseOnUncaughtExceptions=Pause on uncaught exceptions. Click to pause on all exceptions\n\n# LOCALIZATION NOTE (pauseOnExceptions): The pause on exceptions button tooltip\n# when the debugger will pause on all exceptions.\npauseOnExceptions=Pause on all exceptions. Click to ignore exceptions\n\n# LOCALIZATION NOTE (loadingText): The text that is displayed in the script\n# editor when the loading process has started but there is no file to display\n# yet.\nloadingText=Loading\\u2026\n\n# LOCALIZATION NOTE (errorLoadingText2): The text that is displayed in the debugger\n# viewer when there is an error loading a file\nerrorLoadingText2=Error loading this URL: %S\n\n# LOCALIZATION NOTE (addWatchExpressionText): The text that is displayed in the\n# watch expressions list to add a new item.\naddWatchExpressionText=Add watch expression\n\n# LOCALIZATION NOTE (addWatchExpressionButton): The button that is displayed in the\n# variables view popup.\naddWatchExpressionButton=Watch\n\n# LOCALIZATION NOTE (emptyVariablesText): The text that is displayed in the\n# variables pane when there are no variables to display.\nemptyVariablesText=No variables to display\n\n# LOCALIZATION NOTE (scopeLabel): The text that is displayed in the variables\n# pane as a header for each variable scope (e.g. \"Global scope, \"With scope\",\n# etc.).\nscopeLabel=%S scope\n\n# LOCALIZATION NOTE (watchExpressionsScopeLabel): The name of the watch\n# expressions scope. This text is displayed in the variables pane as a header for\n# the watch expressions scope.\nwatchExpressionsScopeLabel=Watch expressions\n\n# LOCALIZATION NOTE (globalScopeLabel): The name of the global scope. This text\n# is added to scopeLabel and displayed in the variables pane as a header for\n# the global scope.\nglobalScopeLabel=Global\n\n# LOCALIZATION NOTE (variablesViewErrorStacktrace): This is the text that is\n# shown before the stack trace in an error.\nvariablesViewErrorStacktrace=Stack trace:\n\n# LOCALIZATION NOTE (variablesViewMoreObjects): the text that is displayed\n# when you have an object preview that does not show all of the elements. At the end of the list\n# you see \"N more...\" in the web console output.\n# This is a semi-colon list of plural forms.\n# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals\n# #1 number of remaining items in the object\n# example: 3 more…\nvariablesViewMoreObjects=#1 more…;#1 more…\n\n# LOCALIZATION NOTE (variablesEditableNameTooltip): The text that is displayed\n# in the variables list on an item with an editable name.\nvariablesEditableNameTooltip=Double click to edit\n\n# LOCALIZATION NOTE (variablesEditableValueTooltip): The text that is displayed\n# in the variables list on an item with an editable value.\nvariablesEditableValueTooltip=Click to change value\n\n# LOCALIZATION NOTE (variablesCloseButtonTooltip): The text that is displayed\n# in the variables list on an item which can be removed.\nvariablesCloseButtonTooltip=Click to remove\n\n# LOCALIZATION NOTE (variablesEditButtonTooltip): The text that is displayed\n# in the variables list on a getter or setter which can be edited.\nvariablesEditButtonTooltip=Click to set value\n\n# LOCALIZATION NOTE (variablesEditableValueTooltip): The text that is displayed\n# in a tooltip on the \"open in inspector\" button in the the variables list for a\n# DOMNode item.\nvariablesDomNodeValueTooltip=Click to select the node in the inspector\n\n# LOCALIZATION NOTE (configurable|...|Tooltip): The text that is displayed\n# in the variables list on certain variables or properties as tooltips.\n# Expanations of what these represent can be found at the following links:\n# https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty\n# https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/isExtensible\n# https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/isFrozen\n# https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/isSealed\n# It's probably best to keep these in English.\nconfigurableTooltip=configurable\nenumerableTooltip=enumerable\nwritableTooltip=writable\nfrozenTooltip=frozen\nsealedTooltip=sealed\nextensibleTooltip=extensible\noverriddenTooltip=overridden\nWebIDLTooltip=WebIDL\n\n# LOCALIZATION NOTE (variablesSeparatorLabel): The text that is displayed\n# in the variables list as a separator between the name and value.\nvariablesSeparatorLabel=:\n\n# LOCALIZATION NOTE (watchExpressionsSeparatorLabel2): The text that is displayed\n# in the watch expressions list as a separator between the code and evaluation.\nwatchExpressionsSeparatorLabel2=\\u0020→\n\n# LOCALIZATION NOTE (functionSearchSeparatorLabel): The text that is displayed\n# in the functions search panel as a separator between function's inferred name\n# and its real name (if available).\nfunctionSearchSeparatorLabel=←\n\n# LOCALIZATION NOTE(symbolSearch.search.functionsPlaceholder): The placeholder\n# text displayed when the user searches for functions in a file\nsymbolSearch.search.functionsPlaceholder=Search functions…\n\n# LOCALIZATION NOTE(symbolSearch.search.variablesPlaceholder): The placeholder\n# text displayed when the user searches for variables in a file\nsymbolSearch.search.variablesPlaceholder=Search variables…\n\n# LOCALIZATION NOTE(symbolSearch.search.key): The shortcut (cmd+shift+o) for\n# searching for a function or variable\nsymbolSearch.search.key=O\n\n# LOCALIZATION NOTE(symbolSearch.searchModifier.regex): A search option\n# when searching text in a file\nsymbolSearch.searchModifier.regex=Regex\n\n# LOCALIZATION NOTE(symbolSearch.searchModifier.caseSensitive): A search option\n# when searching text in a file\nsymbolSearch.searchModifier.caseSensitive=Case sensitive\n\n# LOCALIZATION NOTE(symbolSearch.searchModifier.wholeWord): A search option\n# when searching text in a file\nsymbolSearch.searchModifier.wholeWord=Whole word\n\n# LOCALIZATION NOTE (resumptionOrderPanelTitle): This is the text that appears\n# as a description in the notification panel popup, when multiple debuggers are\n# open in separate tabs and the user tries to resume them in the wrong order.\n# The substitution parameter is the URL of the last paused window that must be\n# resumed first.\nresumptionOrderPanelTitle=There are one or more paused debuggers. Please resume the most-recently paused debugger first at: %S\n\nvariablesViewOptimizedOut=(optimized away)\nvariablesViewUninitialized=(uninitialized)\nvariablesViewMissingArgs=(unavailable)\n\nanonymousSourcesLabel=Anonymous Sources\n\nexperimental=This is an experimental feature\n\n# LOCALIZATION NOTE (whyPaused.debuggerStatement): The text that is displayed\n# in a info block explaining how the debugger is currently paused due to a `debugger`\n# statement in the code\nwhyPaused.debuggerStatement=Paused on debugger statement\n\n# LOCALIZATION NOTE (whyPaused.breakpoint): The text that is displayed\n# in a info block explaining how the debugger is currently paused on a breakpoint\nwhyPaused.breakpoint=Paused on breakpoint\n\n# LOCALIZATION NOTE (whyPaused.exception): The text that is displayed\n# in a info block explaining how the debugger is currently paused on an exception\nwhyPaused.exception=Paused on exception\n\n# LOCALIZATION NOTE (whyPaused.resumeLimit): The text that is displayed\n# in a info block explaining how the debugger is currently paused while stepping\n# in or out of the stack\nwhyPaused.resumeLimit=Paused while stepping\n\n# LOCALIZATION NOTE (whyPaused.pauseOnDOMEvents): The text that is displayed\n# in a info block explaining how the debugger is currently paused on a\n# dom event\nwhyPaused.pauseOnDOMEvents=Paused on event listener\n\n# LOCALIZATION NOTE (whyPaused.breakpointConditionThrown): The text that is displayed\n# in an info block when evaluating a conditional breakpoint throws an error\nwhyPaused.breakpointConditionThrown=Error with conditional breakpoint\n\n# LOCALIZATION NOTE (whyPaused.xhr): The text that is displayed\n# in a info block explaining how the debugger is currently paused on an\n# xml http request\nwhyPaused.xhr=Paused on XMLHttpRequest\n\n# LOCALIZATION NOTE (whyPaused.promiseRejection): The text that is displayed\n# in a info block explaining how the debugger is currently paused on a\n# promise rejection\nwhyPaused.promiseRejection=Paused on promise rejection\n\n# LOCALIZATION NOTE (whyPaused.assert): The text that is displayed\n# in a info block explaining how the debugger is currently paused on an\n# assert\nwhyPaused.assert=Paused on assertion\n\n# LOCALIZATION NOTE (whyPaused.debugCommand): The text that is displayed\n# in a info block explaining how the debugger is currently paused on a\n# debugger statement\nwhyPaused.debugCommand=Paused on debugged function\n\n# LOCALIZATION NOTE (whyPaused.other): The text that is displayed\n# in a info block explaining how the debugger is currently paused on an event\n# listener breakpoint set\nwhyPaused.other=Debugger paused\n\n# LOCALIZATION NOTE (ctrl): The text that is used for documenting\n# keyboard shortcuts that use the control key\nctrl=Ctrl\n" + module.exports = "# This Source Code Form is subject to the terms of the Mozilla Public\n# License, v. 2.0. If a copy of the MPL was not distributed with this\n# file, You can obtain one at http://mozilla.org/MPL/2.0/.\n\n# LOCALIZATION NOTE These strings are used inside the Debugger\n# which is available from the Web Developer sub-menu -> 'Debugger'.\n# The correct localization of this file might be to keep it in\n# English, or another language commonly spoken among web developers.\n# You want to make that choice consistent across the developer tools.\n# A good criteria is the language in which you'd find the best\n# documentation on web development on the web.\n\n# LOCALIZATION NOTE (collapsePanes): This is the tooltip for the button\n# that collapses the left and right panes in the debugger UI.\ncollapsePanes=Collapse panes\n\n# LOCALIZATION NOTE (copySourceUrl): This is the text that appears in the\n# context menu to copy the source URL of file open.\ncopySourceUrl=Copy Source Url\n\n# LOCALIZATION NOTE (copySourceUrl.accesskey): Access key to copy the source URL of a file from\n# the context menu.\ncopySourceUrl.accesskey=u\n\n# LOCALIZATION NOTE (copyStackTrace): This is the text that appears in the\n# context menu to copy the stack trace methods, file names and row number.\ncopyStackTrace=Copy Stack Trace\n\n# LOCALIZATION NOTE (copyStackTrace.accesskey): Access key to copy the stack trace data from\n# the context menu.\ncopyStackTrace.accesskey=c\n\n# LOCALIZATION NOTE (expandPanes): This is the tooltip for the button\n# that expands the left and right panes in the debugger UI.\nexpandPanes=Expand panes\n\n# LOCALIZATION NOTE (pauseButtonTooltip): The tooltip that is displayed for the pause\n# button when the debugger is in a running state.\npauseButtonTooltip=Pause %S\n\n# LOCALIZATION NOTE (pausePendingButtonTooltip): The tooltip that is displayed for\n# the pause button after it's been clicked but before the next JavaScript to run.\npausePendingButtonTooltip=Waiting for next execution\n\n# LOCALIZATION NOTE (resumeButtonTooltip): The label that is displayed on the pause\n# button when the debugger is in a paused state.\nresumeButtonTooltip=Resume %S\n\n# LOCALIZATION NOTE (stepOverTooltip): The label that is displayed on the\n# button that steps over a function call.\nstepOverTooltip=Step Over %S\n\n# LOCALIZATION NOTE (stepInTooltip): The label that is displayed on the\n# button that steps into a function call.\nstepInTooltip=Step In %S\n\n# LOCALIZATION NOTE (stepOutTooltip): The label that is displayed on the\n# button that steps out of a function call.\nstepOutTooltip=Step Out %S\n\n# LOCALIZATION NOTE (noWorkersText): The text to display in the workers list\n# when there are no workers.\nnoWorkersText=This page has no workers.\n\n# LOCALIZATION NOTE (noSourcesText): The text to display in the sources list\n# when there are no sources.\nnoSourcesText=This page has no sources.\n\n# LOCALIZATION NOTE (noEventListenersText): The text to display in the events tab\n# when there are no events.\nnoEventListenersText=No event listeners to display\n\n# LOCALIZATION NOTE (eventListenersHeader): The text to display in the events\n# header.\neventListenersHeader=Event Listeners\n\n# LOCALIZATION NOTE (noStackFramesText): The text to display in the call stack tab\n# when there are no stack frames.\nnoStackFramesText=No stack frames to display\n\n# LOCALIZATION NOTE (eventCheckboxTooltip): The tooltip text to display when\n# the user hovers over the checkbox used to toggle an event breakpoint.\neventCheckboxTooltip=Toggle breaking on this event\n\n# LOCALIZATION NOTE (eventOnSelector): The text to display in the events tab\n# for every event item, between the event type and event selector.\neventOnSelector=on\n\n# LOCALIZATION NOTE (eventInSource): The text to display in the events tab\n# for every event item, between the event selector and listener's owner source.\neventInSource=in\n\n# LOCALIZATION NOTE (eventNodes): The text to display in the events tab when\n# an event is listened on more than one target node.\neventNodes=%S nodes\n\n# LOCALIZATION NOTE (eventNative): The text to display in the events tab when\n# a listener is added from plugins, thus getting translated to native code.\neventNative=[native code]\n\n# LOCALIZATION NOTE (*Events): The text to display in the events tab for\n# each group of sub-level event entries.\nanimationEvents=Animation\naudioEvents=Audio\nbatteryEvents=Battery\nclipboardEvents=Clipboard\ncompositionEvents=Composition\ndeviceEvents=Device\ndisplayEvents=Display\ndragAndDropEvents=Drag and Drop\ngamepadEvents=Gamepad\nindexedDBEvents=IndexedDB\ninteractionEvents=Interaction\nkeyboardEvents=Keyboard\nmediaEvents=HTML5 Media\nmouseEvents=Mouse\nmutationEvents=Mutation\nnavigationEvents=Navigation\npointerLockEvents=Pointer Lock\nsensorEvents=Sensor\nstorageEvents=Storage\ntimeEvents=Time\ntouchEvents=Touch\notherEvents=Other\n\n# LOCALIZATION NOTE (blackboxCheckboxTooltip2): The tooltip text to display when\n# the user hovers over the checkbox used to toggle blackboxing its associated\n# source.\nblackboxCheckboxTooltip2=Toggle blackboxing\n\n# LOCALIZATION NOTE (sources.search.key2): Key shortcut to open the search for\n# searching all the source files the debugger has seen.\nsources.search.key2=CmdOrCtrl+P\n\n# LOCALIZATION NOTE (sources.search.alt.key): A second key shortcut to open the\n# search for searching all the source files the debugger has seen.\nsources.search.alt.key=CmdOrCtrl+O\n\n# LOCALIZATION NOTE (sources.noSourcesAvailable): Text shown when the debugger\n# does not have any sources.\nsources.noSourcesAvailable=This page has no sources\n\n# LOCALIZATION NOTE (sourceSearch.search.key2): Key shortcut to open the search\n# for searching within a the currently opened files in the editor\nsourceSearch.search.key2=CmdOrCtrl+F\n\n# LOCALIZATION NOTE (sourceSearch.search.placeholder): placeholder text in\n# the source search input bar\nsourceSearch.search.placeholder=Search in file…\n\n# LOCALIZATION NOTE (sourceSearch.search.again.key2): Key shortcut to highlight\n# the next occurrence of the last search triggered from a source search\nsourceSearch.search.again.key2=CmdOrCtrl+G\n\n# LOCALIZATION NOTE (sourceSearch.search.againPrev.key2): Key shortcut to highlight\n# the previous occurrence of the last search triggered from a source search\nsourceSearch.search.againPrev.key2=CmdOrCtrl+Shift+G\n\n# LOCALIZATION NOTE (sourceSearch.resultsSummary1): Shows a summary of\n# the number of matches for autocomplete\nsourceSearch.resultsSummary1=%d results\n\n# LOCALIZATION NOTE (noMatchingStringsText): The text to display in the\n# global search results when there are no matching strings after filtering.\nnoMatchingStringsText=No matches found\n\n# LOCALIZATION NOTE (emptySearchText): This is the text that appears in the\n# filter text box when it is empty and the scripts container is selected.\nemptySearchText=Search scripts (%S)\n\n# LOCALIZATION NOTE (emptyVariablesFilterText): This is the text that\n# appears in the filter text box for the variables view container.\nemptyVariablesFilterText=Filter variables\n\n# LOCALIZATION NOTE (emptyPropertiesFilterText): This is the text that\n# appears in the filter text box for the editor's variables view bubble.\nemptyPropertiesFilterText=Filter properties\n\n# LOCALIZATION NOTE (searchPanelFilter): This is the text that appears in the\n# filter panel popup for the filter scripts operation.\nsearchPanelFilter=Filter scripts (%S)\n\n# LOCALIZATION NOTE (searchPanelGlobal): This is the text that appears in the\n# filter panel popup for the global search operation.\nsearchPanelGlobal=Search in all files (%S)\n\n# LOCALIZATION NOTE (searchPanelFunction): This is the text that appears in the\n# filter panel popup for the function search operation.\nsearchPanelFunction=Search for function definition (%S)\n\n# LOCALIZATION NOTE (searchPanelToken): This is the text that appears in the\n# filter panel popup for the token search operation.\nsearchPanelToken=Find in this file (%S)\n\n# LOCALIZATION NOTE (searchPanelGoToLine): This is the text that appears in the\n# filter panel popup for the line search operation.\nsearchPanelGoToLine=Go to line (%S)\n\n# LOCALIZATION NOTE (searchPanelVariable): This is the text that appears in the\n# filter panel popup for the variables search operation.\nsearchPanelVariable=Filter variables (%S)\n\n# LOCALIZATION NOTE (breakpointMenuItem): The text for all the elements that\n# are displayed in the breakpoints menu item popup.\nbreakpointMenuItem.setConditional=Configure conditional breakpoint\nbreakpointMenuItem.enableSelf=Enable breakpoint\nbreakpointMenuItem.disableSelf=Disable breakpoint\nbreakpointMenuItem.deleteSelf=Remove breakpoint\nbreakpointMenuItem.enableOthers=Enable others\nbreakpointMenuItem.disableOthers=Disable others\nbreakpointMenuItem.deleteOthers=Remove others\nbreakpointMenuItem.enableAll=Enable all breakpoints\nbreakpointMenuItem.disableAll=Disable all breakpoints\nbreakpointMenuItem.deleteAll=Remove all breakpoints\n\n# LOCALIZATION NOTE (breakpoints.header): Breakpoints right sidebar pane header.\nbreakpoints.header=Breakpoints\n\n# LOCALIZATION NOTE (breakpoints.none): The text that appears when there are\n# no breakpoints present\nbreakpoints.none=No Breakpoints\n\n# LOCALIZATION NOTE (breakpoints.enable): The text that may appear as a tooltip\n# when hovering over the 'disable breakpoints' switch button in right sidebar\nbreakpoints.enable=Enable Breakpoints\n\n# LOCALIZATION NOTE (breakpoints.disable): The text that may appear as a tooltip\n# when hovering over the 'disable breakpoints' switch button in right sidebar\nbreakpoints.disable=Disable Breakpoints\n\n# LOCALIZATION NOTE (breakpoints.removeBreakpointTooltip): The tooltip that is displayed\n# for remove breakpoint button in right sidebar\nbreakpoints.removeBreakpointTooltip=Remove Breakpoint\n\n# LOCALIZATION NOTE (callStack.header): Call Stack right sidebar pane header.\ncallStack.header=Call Stack\n\n# LOCALIZATION NOTE (callStack.notPaused): Call Stack right sidebar pane\n# message when not paused.\ncallStack.notPaused=Not Paused\n\n# LOCALIZATION NOTE (callStack.collapse): Call Stack right sidebar pane\n# message to hide some of the frames that are shown.\ncallStack.collapse=Collapse Rows\n\n# LOCALIZATION NOTE (callStack.expand): Call Stack right sidebar pane\n# message to show more of the frames.\ncallStack.expand=Expand Rows\n\n# LOCALIZATION NOTE (editor.searchResults): Editor Search bar message\n# for the summarizing the selected search result. e.g. 5 of 10 results.\neditor.searchResults=%d of %d results\n\n# LOCALIZATION NOTE (sourceSearch.singleResult): Copy shown when there is one result.\neditor.singleResult=1 result\n\n# LOCALIZATION NOTE (editor.noResults): Editor Search bar message\n# for when no results found.\neditor.noResults=no results\n\n# LOCALIZATION NOTE (editor.searchResults.nextResult): Editor Search bar\n# tooltip for traversing to the Next Result\neditor.searchResults.nextResult=Next Result\n\n# LOCALIZATION NOTE (editor.searchResults.prevResult): Editor Search bar\n# tooltip for traversing to the Previous Result\neditor.searchResults.prevResult=Previous Result\n\n# LOCALIZATION NOTE (editor.searchTypeToggleTitle): Search bar title for\n# toggling search type buttons(function search, variable search)\neditor.searchTypeToggleTitle=Search for:\n\n# LOCALIZATION NOTE (editor.addBreakpoint): Editor gutter context menu item\n# for adding a breakpoint on a line.\neditor.addBreakpoint=Add Breakpoint\n\n# LOCALIZATION NOTE (editor.disableBreakpoint): Editor gutter context menu item\n# for disabling a breakpoint on a line.\neditor.disableBreakpoint=Disable Breakpoint\n\n# LOCALIZATION NOTE (editor.enableBreakpoint): Editor gutter context menu item\n# for enabling a breakpoint on a line.\neditor.enableBreakpoint=Enable Breakpoint\n\n# LOCALIZATION NOTE (editor.removeBreakpoint): Editor gutter context menu item\n# for removing a breakpoint on a line.\neditor.removeBreakpoint=Remove Breakpoint\n\n# LOCALIZATION NOTE (editor.editBreakpoint): Editor gutter context menu item\n# for setting a breakpoint condition on a line.\neditor.editBreakpoint=Edit Breakpoint\n\n# LOCALIZATION NOTE (editor.addConditionalBreakpoint): Editor gutter context\n# menu item for adding a breakpoint condition on a line.\neditor.addConditionalBreakpoint=Add Conditional Breakpoint\n\n# LOCALIZATION NOTE (editor.conditionalPanel.placeholder): Placeholder text for\n# input element inside ConditionalPanel component\neditor.conditionalPanel.placeholder=This breakpoint will pause when the expression is true\n\n# LOCALIZATION NOTE (editor.conditionalPanel.placeholder): Tooltip text for\n# close button inside ConditionalPanel component\neditor.conditionalPanel.close=Cancel edit breakpoint and close\n\n# LOCALIZATION NOTE (editor.jumpToMappedLocation1): Context menu item\n# for navigating to a source mapped location\neditor.jumpToMappedLocation1=Jump to %S location\n\n# LOCALIZATION NOTE (generated): Source Map term for a server source location\ngenerated=generated\n\n# LOCALIZATION NOTE (original): Source Map term for a debugger UI source location\noriginal=original\n\n# LOCALIZATION NOTE (expressions.placeholder): Placeholder text for expression\n# input element\nexpressions.placeholder=Add Watch Expression\n\n# LOCALIZATION NOTE (sourceTabs.closeTab): Editor source tab context menu item\n# for closing the selected tab below the mouse.\nsourceTabs.closeTab=Close tab\n\n# LOCALIZATION NOTE (sourceTabs.closeTab.accesskey): Access key to close the currently select\n# source tab from the editor context menu item.\nsourceTabs.closeTab.accesskey=c\n\n# LOCALIZATION NOTE (sourceTabs.closeOtherTabs): Editor source tab context menu item\n# for closing the other tabs.\nsourceTabs.closeOtherTabs=Close others\n\n# LOCALIZATION NOTE (sourceTabs.closeOtherTabs.accesskey): Access key to close other source tabs\n# from the editor context menu.\nsourceTabs.closeOtherTabs.accesskey=o\n\n# LOCALIZATION NOTE (sourceTabs.closeTabsToEnd): Editor source tab context menu item\n# for closing the tabs to the end (the right for LTR languages) of the selected tab.\nsourceTabs.closeTabsToEnd=Close tabs to the right\n\n# LOCALIZATION NOTE (sourceTabs.closeTabsToEnd.accesskey): Access key to close source tabs\n# after the selected tab from the editor context menu.\nsourceTabs.closeTabsToEnd.accesskey=e\n\n# LOCALIZATION NOTE (sourceTabs.closeAllTabs): Editor source tab context menu item\n# for closing all tabs.\nsourceTabs.closeAllTabs=Close all tabs\n\n# LOCALIZATION NOTE (sourceTabs.closeAllTabs.accesskey): Access key to close all tabs from the\n# editor context menu.\nsourceTabs.closeAllTabs.accesskey=a\n\n# LOCALIZATION NOTE (sourceTabs.revealInTree): Editor source tab context menu item\n# for revealing source in tree.\nsourceTabs.revealInTree=Reveal in Tree\n\n# LOCALIZATION NOTE (sourceTabs.revealInTree.accesskey): Access key to reveal a source in the\n# tree from the context menu.\nsourceTabs.revealInTree.accesskey=r\n\n# LOCALIZATION NOTE (sourceTabs.copyLink): Editor source tab context menu item\n# for copying a link address.\nsourceTabs.copyLink=Copy Link Address\n\n# LOCALIZATION NOTE (sourceTabs.copyLink.accesskey): Access key to copy a link addresss from the\n# editor context menu.\nsourceTabs.copyLink.accesskey=l\n\n# LOCALIZATION NOTE (sourceTabs.prettyPrint): Editor source tab context menu item\n# for pretty printing the source.\nsourceTabs.prettyPrint=Pretty Print Source\n\n# LOCALIZATION NOTE (sourceTabs.prettyPrint.accesskey): Access key to pretty print a source from\n# the editor context menu.\nsourceTabs.prettyPrint.accesskey=p\n\n# LOCALIZATION NOTE (sourceFooter.blackbox): Tooltip text associated\n# with the blackbox button\nsourceFooter.blackbox=Blackbox Source\n\n# LOCALIZATION NOTE (sourceFooter.unblackbox): Tooltip text associated\n# with the blackbox button\nsourceFooter.unblackbox=Unblackbox Source\n\n# LOCALIZATION NOTE (sourceFooter.blackbox.accesskey): Access key to blackbox\n# an associated source\nsourceFooter.blackbox.accesskey=b\n\n# LOCALIZATION NOTE (sourceFooter.blackboxed): Text associated\n# with a blackboxed source\nsourceFooter.blackboxed=Blackboxed Source\n\n# LOCALIZATION NOTE (sourceTabs.closeTabButtonTooltip): The tooltip that is displayed\n# for close tab button in source tabs.\nsourceTabs.closeTabButtonTooltip=Close tab\n\n# LOCALIZATION NOTE (sourceTabs.newTabButtonTooltip): The tooltip that is displayed for\n# new tab button in source tabs.\nsourceTabs.newTabButtonTooltip=Search for sources (%S)\n\n# LOCALIZATION NOTE (scopes.header): Scopes right sidebar pane header.\nscopes.header=Scopes\n\n# LOCALIZATION NOTE (scopes.notAvailable): Scopes right sidebar pane message\n# for when the debugger is paused, but there isn't pause data.\nscopes.notAvailable=Scopes Unavailable\n\n# LOCALIZATION NOTE (scopes.notPaused): Scopes right sidebar pane message\n# for when the debugger is not paused.\nscopes.notPaused=Not Paused\n\n# LOCALIZATION NOTE (scopes.block): Refers to a block of code in\n# the scopes pane when the debugger is paused.\nscopes.block=Block\n\n# LOCALIZATION NOTE (sources.header): Sources left sidebar header\nsources.header=Sources\n\n# LOCALIZATION NOTE (sources.search): Sources left sidebar prompt\n# e.g. Cmd+P to search. On a mac, we use the command unicode character.\n# On windows, it's ctrl.\nsources.search=%S to search\n\n# LOCALIZATION NOTE (watchExpressions.header): Watch Expressions right sidebar\n# pane header.\nwatchExpressions.header=Watch Expressions\n\n# LOCALIZATION NOTE (watchExpressions.refreshButton): Watch Expressions header\n# button for refreshing the expressions.\nwatchExpressions.refreshButton=Refresh\n\n# LOCALIZATION NOTE (welcome.search): The center pane welcome panel's\n# search prompt. e.g. cmd+p to search for files. On windows, it's ctrl, on\n# a mac we use the unicode character.\nwelcome.search=%S to search for sources\n\n# LOCALIZATION NOTE (sourceSearch.search): The center pane Source Search\n# prompt for searching for files.\nsourceSearch.search=Search Sources…\n\n# LOCALIZATION NOTE (sourceSearch.noResults): The center pane Source Search\n# message when the query did not match any of the sources.\nsourceSearch.noResults=No files matching %S found\n\n# LOCALIZATION NOTE (ignoreExceptions): The pause on exceptions button tooltip\n# when the debugger will not pause on exceptions.\nignoreExceptions=Ignore exceptions. Click to pause on uncaught exceptions\n\n# LOCALIZATION NOTE (pauseOnUncaughtExceptions): The pause on exceptions button\n# tooltip when the debugger will pause on uncaught exceptions.\npauseOnUncaughtExceptions=Pause on uncaught exceptions. Click to pause on all exceptions\n\n# LOCALIZATION NOTE (pauseOnExceptions): The pause on exceptions button tooltip\n# when the debugger will pause on all exceptions.\npauseOnExceptions=Pause on all exceptions. Click to ignore exceptions\n\n# LOCALIZATION NOTE (loadingText): The text that is displayed in the script\n# editor when the loading process has started but there is no file to display\n# yet.\nloadingText=Loading\\u2026\n\n# LOCALIZATION NOTE (errorLoadingText2): The text that is displayed in the debugger\n# viewer when there is an error loading a file\nerrorLoadingText2=Error loading this URL: %S\n\n# LOCALIZATION NOTE (addWatchExpressionText): The text that is displayed in the\n# watch expressions list to add a new item.\naddWatchExpressionText=Add watch expression\n\n# LOCALIZATION NOTE (addWatchExpressionButton): The button that is displayed in the\n# variables view popup.\naddWatchExpressionButton=Watch\n\n# LOCALIZATION NOTE (emptyVariablesText): The text that is displayed in the\n# variables pane when there are no variables to display.\nemptyVariablesText=No variables to display\n\n# LOCALIZATION NOTE (scopeLabel): The text that is displayed in the variables\n# pane as a header for each variable scope (e.g. \"Global scope, \"With scope\",\n# etc.).\nscopeLabel=%S scope\n\n# LOCALIZATION NOTE (watchExpressionsScopeLabel): The name of the watch\n# expressions scope. This text is displayed in the variables pane as a header for\n# the watch expressions scope.\nwatchExpressionsScopeLabel=Watch expressions\n\n# LOCALIZATION NOTE (globalScopeLabel): The name of the global scope. This text\n# is added to scopeLabel and displayed in the variables pane as a header for\n# the global scope.\nglobalScopeLabel=Global\n\n# LOCALIZATION NOTE (variablesViewErrorStacktrace): This is the text that is\n# shown before the stack trace in an error.\nvariablesViewErrorStacktrace=Stack trace:\n\n# LOCALIZATION NOTE (variablesViewMoreObjects): the text that is displayed\n# when you have an object preview that does not show all of the elements. At the end of the list\n# you see \"N more...\" in the web console output.\n# This is a semi-colon list of plural forms.\n# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals\n# #1 number of remaining items in the object\n# example: 3 more…\nvariablesViewMoreObjects=#1 more…;#1 more…\n\n# LOCALIZATION NOTE (variablesEditableNameTooltip): The text that is displayed\n# in the variables list on an item with an editable name.\nvariablesEditableNameTooltip=Double click to edit\n\n# LOCALIZATION NOTE (variablesEditableValueTooltip): The text that is displayed\n# in the variables list on an item with an editable value.\nvariablesEditableValueTooltip=Click to change value\n\n# LOCALIZATION NOTE (variablesCloseButtonTooltip): The text that is displayed\n# in the variables list on an item which can be removed.\nvariablesCloseButtonTooltip=Click to remove\n\n# LOCALIZATION NOTE (variablesEditButtonTooltip): The text that is displayed\n# in the variables list on a getter or setter which can be edited.\nvariablesEditButtonTooltip=Click to set value\n\n# LOCALIZATION NOTE (variablesEditableValueTooltip): The text that is displayed\n# in a tooltip on the \"open in inspector\" button in the the variables list for a\n# DOMNode item.\nvariablesDomNodeValueTooltip=Click to select the node in the inspector\n\n# LOCALIZATION NOTE (configurable|...|Tooltip): The text that is displayed\n# in the variables list on certain variables or properties as tooltips.\n# Expanations of what these represent can be found at the following links:\n# https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty\n# https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/isExtensible\n# https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/isFrozen\n# https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/isSealed\n# It's probably best to keep these in English.\nconfigurableTooltip=configurable\nenumerableTooltip=enumerable\nwritableTooltip=writable\nfrozenTooltip=frozen\nsealedTooltip=sealed\nextensibleTooltip=extensible\noverriddenTooltip=overridden\nWebIDLTooltip=WebIDL\n\n# LOCALIZATION NOTE (variablesSeparatorLabel): The text that is displayed\n# in the variables list as a separator between the name and value.\nvariablesSeparatorLabel=:\n\n# LOCALIZATION NOTE (watchExpressionsSeparatorLabel2): The text that is displayed\n# in the watch expressions list as a separator between the code and evaluation.\nwatchExpressionsSeparatorLabel2=\\u0020→\n\n# LOCALIZATION NOTE (functionSearchSeparatorLabel): The text that is displayed\n# in the functions search panel as a separator between function's inferred name\n# and its real name (if available).\nfunctionSearchSeparatorLabel=←\n\n# LOCALIZATION NOTE(symbolSearch.search.functionsPlaceholder): The placeholder\n# text displayed when the user searches for functions in a file\nsymbolSearch.search.functionsPlaceholder=Search functions…\n\n# LOCALIZATION NOTE(symbolSearch.search.variablesPlaceholder): The placeholder\n# text displayed when the user searches for variables in a file\nsymbolSearch.search.variablesPlaceholder=Search variables…\n\n# LOCALIZATION NOTE(symbolSearch.search.key2): The Key Shortcut for\n# searching for a function or variable\nsymbolSearch.search.key2=CmdOrCtrl+Shift+O\n\n# LOCALIZATION NOTE(symbolSearch.searchModifier.regex): A search option\n# when searching text in a file\nsymbolSearch.searchModifier.regex=Regex\n\n# LOCALIZATION NOTE(symbolSearch.searchModifier.caseSensitive): A search option\n# when searching text in a file\nsymbolSearch.searchModifier.caseSensitive=Case sensitive\n\n# LOCALIZATION NOTE(symbolSearch.searchModifier.wholeWord): A search option\n# when searching text in a file\nsymbolSearch.searchModifier.wholeWord=Whole word\n\n# LOCALIZATION NOTE (resumptionOrderPanelTitle): This is the text that appears\n# as a description in the notification panel popup, when multiple debuggers are\n# open in separate tabs and the user tries to resume them in the wrong order.\n# The substitution parameter is the URL of the last paused window that must be\n# resumed first.\nresumptionOrderPanelTitle=There are one or more paused debuggers. Please resume the most-recently paused debugger first at: %S\n\nvariablesViewOptimizedOut=(optimized away)\nvariablesViewUninitialized=(uninitialized)\nvariablesViewMissingArgs=(unavailable)\n\nanonymousSourcesLabel=Anonymous Sources\n\nexperimental=This is an experimental feature\n\n# LOCALIZATION NOTE (whyPaused.debuggerStatement): The text that is displayed\n# in a info block explaining how the debugger is currently paused due to a `debugger`\n# statement in the code\nwhyPaused.debuggerStatement=Paused on debugger statement\n\n# LOCALIZATION NOTE (whyPaused.breakpoint): The text that is displayed\n# in a info block explaining how the debugger is currently paused on a breakpoint\nwhyPaused.breakpoint=Paused on breakpoint\n\n# LOCALIZATION NOTE (whyPaused.exception): The text that is displayed\n# in a info block explaining how the debugger is currently paused on an exception\nwhyPaused.exception=Paused on exception\n\n# LOCALIZATION NOTE (whyPaused.resumeLimit): The text that is displayed\n# in a info block explaining how the debugger is currently paused while stepping\n# in or out of the stack\nwhyPaused.resumeLimit=Paused while stepping\n\n# LOCALIZATION NOTE (whyPaused.pauseOnDOMEvents): The text that is displayed\n# in a info block explaining how the debugger is currently paused on a\n# dom event\nwhyPaused.pauseOnDOMEvents=Paused on event listener\n\n# LOCALIZATION NOTE (whyPaused.breakpointConditionThrown): The text that is displayed\n# in an info block when evaluating a conditional breakpoint throws an error\nwhyPaused.breakpointConditionThrown=Error with conditional breakpoint\n\n# LOCALIZATION NOTE (whyPaused.xhr): The text that is displayed\n# in a info block explaining how the debugger is currently paused on an\n# xml http request\nwhyPaused.xhr=Paused on XMLHttpRequest\n\n# LOCALIZATION NOTE (whyPaused.promiseRejection): The text that is displayed\n# in a info block explaining how the debugger is currently paused on a\n# promise rejection\nwhyPaused.promiseRejection=Paused on promise rejection\n\n# LOCALIZATION NOTE (whyPaused.assert): The text that is displayed\n# in a info block explaining how the debugger is currently paused on an\n# assert\nwhyPaused.assert=Paused on assertion\n\n# LOCALIZATION NOTE (whyPaused.debugCommand): The text that is displayed\n# in a info block explaining how the debugger is currently paused on a\n# debugger statement\nwhyPaused.debugCommand=Paused on debugged function\n\n# LOCALIZATION NOTE (whyPaused.other): The text that is displayed\n# in a info block explaining how the debugger is currently paused on an event\n# listener breakpoint set\nwhyPaused.other=Debugger paused\n\n# LOCALIZATION NOTE (ctrl): The text that is used for documenting\n# keyboard shortcuts that use the control key\nctrl=Ctrl\n" /***/ }, /* 961 */, @@ -45944,6 +46149,35 @@ return /******/ (function(modules) { // webpackBootstrap /***/ }, /* 992 */ +/***/ function(module, exports) { + + "use strict"; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + exports.timing = timing; + /** + * Redux middleware that sets performance markers for all actions such that they + * will appear in performance tooling under the User Timing API + */ + + var mark = window.performance && window.performance.mark ? window.performance.mark.bind(window.performance) : () => {}; + + var measure = window.performance && window.performance.measure ? window.performance.measure.bind(window.performance) : () => {}; + + function timing(store) { + return next => action => { + mark(`${action.type}_start`); + var result = next(action); + mark(`${action.type}_end`); + measure(`${action.type}`, `${action.type}_start`, `${action.type}_end`); + return result; + }; + } + +/***/ }, +/* 993 */ /***/ function(module, exports) { 'use strict'; @@ -46073,51 +46307,28 @@ return /******/ (function(modules) { // webpackBootstrap } /***/ }, -/* 993 */ +/* 994 */ /***/ function(module, exports, __webpack_require__) { "use strict"; - Object.defineProperty(exports, "__esModule", { - value: true - }); - exports.SourceEditorUtils = exports.SourceEditor = undefined; - - var _sourceEditor = __webpack_require__(994); - - var _sourceEditor2 = _interopRequireDefault(_sourceEditor); - - var _utils = __webpack_require__(995); + var SourceEditor = __webpack_require__(995); + var SourceEditorUtils = __webpack_require__(996); - var SourceEditorUtils = _interopRequireWildcard(_utils); - - function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } - - function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - - exports.SourceEditor = _sourceEditor2.default; - exports.SourceEditorUtils = SourceEditorUtils; + module.exports = { SourceEditor, SourceEditorUtils }; /***/ }, -/* 994 */ +/* 995 */ /***/ function(module, exports) { - module.exports = __WEBPACK_EXTERNAL_MODULE_994__; + module.exports = __WEBPACK_EXTERNAL_MODULE_995__; /***/ }, -/* 995 */ +/* 996 */ /***/ function(module, exports) { "use strict"; - Object.defineProperty(exports, "__esModule", { - value: true - }); - exports.removeLineClass = removeLineClass; - exports.clearLineClass = clearLineClass; - exports.getTextForLine = getTextForLine; - exports.getCursorLine = getCursorLine; - exports.resizeBreakpointGutter = resizeBreakpointGutter; function forEachLine(codeMirror, iter) { codeMirror.doc.iter(0, codeMirror.lineCount(), iter); } @@ -46153,44 +46364,83 @@ return /******/ (function(modules) { // webpackBootstrap breakpoints.style.width = `${lineNumbers.clientWidth}px`; } + module.exports = { + removeLineClass, + clearLineClass, + getTextForLine, + getCursorLine, + resizeBreakpointGutter + }; + /***/ }, -/* 996 */ +/* 997 */ /***/ function(module, exports) { module.exports = "" /***/ }, -/* 997 */ +/* 998 */ +/***/ function(module, exports) { + + module.exports = "" + +/***/ }, +/* 999 */ /***/ function(module, exports) { module.exports = "" /***/ }, -/* 998 */ +/* 1000 */ /***/ function(module, exports) { module.exports = "" /***/ }, -/* 999 */ +/* 1001 */ /***/ function(module, exports) { module.exports = "icon" /***/ }, -/* 1000 */ +/* 1002 */ +/***/ function(module, exports) { + + module.exports = "" + +/***/ }, +/* 1003 */ +/***/ function(module, exports) { + + module.exports = "" + +/***/ }, +/* 1004 */ +/***/ function(module, exports) { + + module.exports = "" + +/***/ }, +/* 1005 */ +/***/ function(module, exports) { + + // removed by extract-text-webpack-plugin + +/***/ }, +/* 1006 */, +/* 1007 */ /***/ function(module, exports, __webpack_require__) { "use strict"; - var Tree = __webpack_require__(1001); + var Tree = __webpack_require__(1008); module.exports = { Tree }; /***/ }, -/* 1001 */ +/* 1008 */ /***/ function(module, exports, __webpack_require__) { "use strict"; @@ -46756,14 +47006,14 @@ return /******/ (function(modules) { // webpackBootstrap }); /***/ }, -/* 1002 */ +/* 1009 */ /***/ function(module, exports) { // removed by extract-text-webpack-plugin /***/ }, -/* 1003 */, -/* 1004 */ +/* 1010 */, +/* 1011 */ /***/ function(module, exports, __webpack_require__) { "use strict"; @@ -46780,19 +47030,25 @@ return /******/ (function(modules) { // webpackBootstrap var _Svg2 = _interopRequireDefault(_Svg); + var _classnames = __webpack_require__(175); + + var _classnames2 = _interopRequireDefault(_classnames); + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } var ReactDOM = __webpack_require__(22); - var breakpointSvg = document.createElement("div"); - ReactDOM.render((0, _Svg2.default)("breakpoint"), breakpointSvg); + var breakpointSvg = document.createElement("span"); + ReactDOM.render((0, _Svg2.default)("column-breakpoint"), breakpointSvg); - function makeBookmark() { - var widget = document.createElement("span"); - widget.innerText = "+"; - widget.classList.add("inline-bp"); - return widget; + function makeBookmark(isDisabled) { + var bp = breakpointSvg.cloneNode(true); + bp.className = (0, _classnames2.default)("editor column-breakpoint", { + "breakpoint-disabled": isDisabled + }); + + return bp; } class ColumnBreakpoint extends _react.Component { @@ -46815,7 +47071,7 @@ return /******/ (function(modules) { // webpackBootstrap var column = bp.location.column; var editor = this.props.editor; - var widget = makeBookmark(); + var widget = makeBookmark(bp.disabled); var bookmark = editor.setBookmark({ line, ch: column }, { widget }); this.bookmark = bookmark; } @@ -46830,6 +47086,9 @@ return /******/ (function(modules) { // webpackBootstrap this.addBreakpoint(); } componentDidUpdate() { + if (this.bookmark) { + this.bookmark.clear(); + } this.addBreakpoint(); } componentWillUnmount() { @@ -46844,17 +47103,12 @@ return /******/ (function(modules) { // webpackBootstrap } } - ColumnBreakpoint.propTypes = { - breakpoint: _react.PropTypes.object.isRequired, - editor: _react.PropTypes.object.isRequired - }; - ColumnBreakpoint.displayName = "ColumnBreakpoint"; exports.default = ColumnBreakpoint; /***/ }, -/* 1005 */ +/* 1012 */ /***/ function(module, exports, __webpack_require__) { "use strict"; @@ -46869,11 +47123,13 @@ return /******/ (function(modules) { // webpackBootstrap var _reactRedux = __webpack_require__(151); - var _Frame = __webpack_require__(1006); + var _reselect = __webpack_require__(993); + + var _Frame = __webpack_require__(1013); var _Frame2 = _interopRequireDefault(_Frame); - var _Group2 = __webpack_require__(1008); + var _Group2 = __webpack_require__(1015); var _Group3 = _interopRequireDefault(_Group2); @@ -46881,11 +47137,13 @@ return /******/ (function(modules) { // webpackBootstrap var _actions2 = _interopRequireDefault(_actions); - var _frame = __webpack_require__(1007); + var _frame = __webpack_require__(1014); + + var _clipboard = __webpack_require__(423); var _selectors = __webpack_require__(242); - __webpack_require__(1011); + __webpack_require__(1018); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } @@ -46908,6 +47166,7 @@ return /******/ (function(modules) { // webpackBootstrap }; this.toggleFramesDisplay = this.toggleFramesDisplay.bind(this); + this.copyStackTrace = this.copyStackTrace.bind(this); } shouldComponentUpdate(nextProps, nextState) { @@ -46931,6 +47190,13 @@ return /******/ (function(modules) { // webpackBootstrap return frames.slice(0, numFramesToShow); } + copyStackTrace() { + var frames = this.props.frames; + + var framesToCopy = frames.map(f => (0, _frame.formatCopyName)(f)).join("\n"); + (0, _clipboard.copyToTheClipboard)(framesToCopy); + } + renderFrames(frames) { var _props2 = this.props, selectFrame = _props2.selectFrame, @@ -46939,14 +47205,17 @@ return /******/ (function(modules) { // webpackBootstrap var framesOrGroups = this.truncateFrames((0, _frame.collapseFrames)(frames)); + return _react.DOM.ul({}, framesOrGroups.map(frameOrGroup => frameOrGroup.id ? FrameComponent({ frame: frameOrGroup, + copyStackTrace: this.copyStackTrace, frames, selectFrame, selectedFrame, key: frameOrGroup.id }) : Group({ group: frameOrGroup, + copyStackTrace: this.copyStackTrace, selectFrame, selectedFrame, key: frameOrGroup[0].id @@ -46984,27 +47253,25 @@ return /******/ (function(modules) { // webpackBootstrap Frames.displayName = "Frames"; - function getSourceForFrame(state, frame) { - return (0, _selectors.getSource)(state, frame.location.sourceId); + function getSourceForFrame(sources, frame) { + return (0, _selectors.getSourceInSources)(sources, frame.location.sourceId); } - function appendSource(state, frame) { + function appendSource(sources, frame) { return Object.assign({}, frame, { - source: getSourceForFrame(state, frame).toJS() + source: getSourceForFrame(sources, frame).toJS() }); } - function getAndProcessFrames(state) { - var frames = (0, _selectors.getFrames)(state); + var getAndProcessFrames = (0, _reselect.createSelector)(_selectors.getFrames, _selectors.getSources, (frames, sources) => { if (!frames) { return null; } - frames = frames.toJS().filter(frame => getSourceForFrame(state, frame)).filter(frame => !get(frame, "source.isBlackBoxed")).map(frame => appendSource(state, frame)).map(_frame.annotateFrame); + frames = frames.toJS().filter(frame => getSourceForFrame(sources, frame)).filter(frame => !get(frame, "source.isBlackBoxed")).map(frame => appendSource(sources, frame)).map(_frame.annotateFrame); - // frames = filterFrameworkFrames(frames); return frames; - } + }); exports.default = (0, _reactRedux.connect)(state => ({ frames: getAndProcessFrames(state), @@ -47012,7 +47279,7 @@ return /******/ (function(modules) { // webpackBootstrap }), dispatch => (0, _redux.bindActionCreators)(_actions2.default, dispatch))(Frames); /***/ }, -/* 1006 */ +/* 1013 */ /***/ function(module, exports, __webpack_require__) { "use strict"; @@ -47023,8 +47290,6 @@ return /******/ (function(modules) { // webpackBootstrap var _react = __webpack_require__(2); - var _devtoolsLaunchpad = __webpack_require__(131); - var _classnames = __webpack_require__(175); var _classnames2 = _interopRequireDefault(_classnames); @@ -47033,12 +47298,14 @@ return /******/ (function(modules) { // webpackBootstrap var _Svg2 = _interopRequireDefault(_Svg); - var _clipboard = __webpack_require__(423); - - var _frame = __webpack_require__(1007); + var _frame = __webpack_require__(1014); var _source = __webpack_require__(233); + var _FrameMenu = __webpack_require__(1032); + + var _FrameMenu2 = _interopRequireDefault(_FrameMenu); + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function renderFrameTitle(frame, options) { @@ -47046,7 +47313,6 @@ return /******/ (function(modules) { // webpackBootstrap return _react.DOM.div({ className: "title" }, displayName); } - function renderFrameLocation(_ref) { var source = _ref.source, location = _ref.location, @@ -47070,42 +47336,12 @@ return /******/ (function(modules) { // webpackBootstrap super(...arguments); } - onContextMenu(event, frame, frames) { - var copySourceUrlLabel = L10N.getStr("copySourceUrl"); - var copySourceUrlKey = L10N.getStr("copySourceUrl.accesskey"); - var copyStackTraceLabel = L10N.getStr("copyStackTrace"); - var copyStackTraceKey = L10N.getStr("copyStackTrace.accesskey"); - - event.stopPropagation(); - event.preventDefault(); - - var menuOptions = []; - - var source = frame.source; - if (source) { - var copySourceUrl = { - id: "node-menu-copy-source", - label: copySourceUrlLabel, - accesskey: copySourceUrlKey, - disabled: false, - click: () => (0, _clipboard.copyToTheClipboard)(source.url) - }; - - menuOptions.push(copySourceUrl); - } - - var framesToCopy = frames.map(f => (0, _frame.formatCopyName)(f)).join("\n"); - var copyStackTrace = { - id: "node-menu-copy-source", - label: copyStackTraceLabel, - accesskey: copyStackTraceKey, - disabled: false, - click: () => (0, _clipboard.copyToTheClipboard)(framesToCopy) - }; - - menuOptions.push(copyStackTrace); + onContextMenu(event) { + var _props = this.props, + frame = _props.frame, + copyStackTrace = _props.copyStackTrace; - (0, _devtoolsLaunchpad.showMenu)(event, menuOptions); + (0, _FrameMenu2.default)(frame, copyStackTrace, event); } onMouseDown(e, frame, selectedFrame) { @@ -47123,12 +47359,11 @@ return /******/ (function(modules) { // webpackBootstrap } render() { - var _props = this.props, - frame = _props.frame, - frames = _props.frames, - selectedFrame = _props.selectedFrame, - hideLocation = _props.hideLocation, - shouldMapDisplayName = _props.shouldMapDisplayName; + var _props2 = this.props, + frame = _props2.frame, + selectedFrame = _props2.selectedFrame, + hideLocation = _props2.hideLocation, + shouldMapDisplayName = _props2.shouldMapDisplayName; return _react.DOM.li({ @@ -47138,22 +47373,13 @@ return /******/ (function(modules) { // webpackBootstrap }), onMouseDown: e => this.onMouseDown(e, frame, selectedFrame), onKeyUp: e => this.onKeyUp(e, frame, selectedFrame), - onContextMenu: e => this.onContextMenu(e, frame, frames), + onContextMenu: e => this.onContextMenu(e), tabIndex: 0 }, renderFrameTitle(frame, { shouldMapDisplayName }), !hideLocation ? renderFrameLocation(frame) : null); } } exports.default = FrameComponent; - FrameComponent.propTypes = { - frame: _react.PropTypes.object.isRequired, - frames: _react.PropTypes.array, - selectedFrame: _react.PropTypes.object, - selectFrame: _react.PropTypes.func.isRequired, - hideLocation: _react.PropTypes.bool, - shouldMapDisplayName: _react.PropTypes.bool - }; - FrameComponent.defaultProps = { hideLocation: false, shouldMapDisplayName: true @@ -47162,7 +47388,7 @@ return /******/ (function(modules) { // webpackBootstrap FrameComponent.displayName = "Frame"; /***/ }, -/* 1007 */ +/* 1014 */ /***/ function(module, exports, __webpack_require__) { "use strict"; @@ -47177,6 +47403,10 @@ return /******/ (function(modules) { // webpackBootstrap exports.formatCopyName = formatCopyName; exports.collapseFrames = collapseFrames; + var _get = __webpack_require__(67); + + var _get2 = _interopRequireDefault(_get); + var _devtoolsConfig = __webpack_require__(828); var _utils = __webpack_require__(234); @@ -47189,11 +47419,8 @@ return /******/ (function(modules) { // webpackBootstrap function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - var get = __webpack_require__(67); - - function getFrameUrl(frame) { - return get(frame, "source.url", "") || ""; + return (0, _get2.default)(frame, "source.url", "") || ""; } function isBackbone(frame) { @@ -47212,7 +47439,27 @@ return /******/ (function(modules) { // webpackBootstrap return getFrameUrl(frame).match(/webpack\/bootstrap/i); } + function isNodeInternal(frame) { + // starts with "internal/" OR no path, just "timers.js", "url.js" etc + // (normally frameUrl will be a FQ pathname) + return (/(^internal\/|^[^.\/]+\.js)/.test(getFrameUrl(frame)) + ); + } + + function isExpress(frame) { + return (/node_modules\/express/.test(getFrameUrl(frame)) + ); + } + + function isPug(frame) { + return (/node_modules\/pug/.test(getFrameUrl(frame)) + ); + } + function getLibraryFromUrl(frame) { + // @TODO each of these fns calls getFrameUrl, just call it once + // (assuming there's not more complex logic to identify a lib) + if (isBackbone(frame)) { return "Backbone"; } @@ -47228,6 +47475,18 @@ return /******/ (function(modules) { // webpackBootstrap if (isWebpack(frame)) { return "Webpack"; } + + if (isNodeInternal(frame)) { + return "Node"; + } + + if (isExpress(frame)) { + return "Express"; + } + + if (isPug(frame)) { + return "Pug"; + } } var displayNameMap = { @@ -47344,7 +47603,7 @@ return /******/ (function(modules) { // webpackBootstrap var currentGroup = null; var prevItem = null; for (var frame of frames) { - var prevLibrary = get(prevItem, "library"); + var prevLibrary = (0, _get2.default)(prevItem, "library"); if (!currentGroup) { currentGroup = [frame]; @@ -47376,7 +47635,7 @@ return /******/ (function(modules) { // webpackBootstrap } /***/ }, -/* 1008 */ +/* 1015 */ /***/ function(module, exports, __webpack_require__) { "use strict"; @@ -47395,11 +47654,15 @@ return /******/ (function(modules) { // webpackBootstrap var _Svg2 = _interopRequireDefault(_Svg); - var _frame = __webpack_require__(1007); + var _frame = __webpack_require__(1014); - __webpack_require__(1009); + var _FrameMenu = __webpack_require__(1032); + + var _FrameMenu2 = _interopRequireDefault(_FrameMenu); - var _Frame = __webpack_require__(1006); + __webpack_require__(1016); + + var _Frame = __webpack_require__(1013); var _Frame2 = _interopRequireDefault(_Frame); @@ -47407,6 +47670,7 @@ return /******/ (function(modules) { // webpackBootstrap var FrameComponent = (0, _react.createFactory)(_Frame2.default); + function renderFrameLocation(frame) { var library = (0, _frame.getLibraryFromUrl)(frame); if (!library) { @@ -47426,15 +47690,25 @@ return /******/ (function(modules) { // webpackBootstrap self.toggleFrames = this.toggleFrames.bind(this); } + onContextMenu(event) { + var _props = this.props, + group = _props.group, + copyStackTrace = _props.copyStackTrace; + + var frame = group[0]; + (0, _FrameMenu2.default)(frame, copyStackTrace, event); + } + toggleFrames() { this.setState({ expanded: !this.state.expanded }); } renderFrames() { - var _props = this.props, - group = _props.group, - selectFrame = _props.selectFrame, - selectedFrame = _props.selectedFrame; + var _props2 = this.props, + group = _props2.group, + selectFrame = _props2.selectFrame, + selectedFrame = _props2.selectedFrame, + copyStackTrace = _props2.copyStackTrace; var expanded = this.state.expanded; if (!expanded) { @@ -47442,6 +47716,7 @@ return /******/ (function(modules) { // webpackBootstrap } return _react.DOM.div({ className: "frames-list" }, group.map(frame => FrameComponent({ frame, + copyStackTrace, selectFrame, selectedFrame, key: frame.id, @@ -47464,31 +47739,745 @@ return /******/ (function(modules) { // webpackBootstrap render() { var expanded = this.state.expanded; - return _react.DOM.div({ className: (0, _classnames2.default)("frames-group", { expanded }) }, this.renderDescription(), this.renderFrames()); + return _react.DOM.div({ + className: (0, _classnames2.default)("frames-group", { expanded }), + onContextMenu: e => this.onContextMenu(e) + }, this.renderDescription(), this.renderFrames()); } } exports.default = Group; - Group.propTypes = { - group: _react.PropTypes.array.isRequired, - selectFrame: _react.PropTypes.func.isRequired, - selectedFrame: _react.PropTypes.object - }; Group.displayName = "Group"; /***/ }, -/* 1009 */ +/* 1016 */ /***/ function(module, exports) { // removed by extract-text-webpack-plugin /***/ }, -/* 1010 */, -/* 1011 */ +/* 1017 */, +/* 1018 */ /***/ function(module, exports) { // removed by extract-text-webpack-plugin +/***/ }, +/* 1019 */, +/* 1020 */, +/* 1021 */, +/* 1022 */, +/* 1023 */, +/* 1024 */, +/* 1025 */ +/***/ function(module, exports, __webpack_require__) { + + "use strict"; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + var _react = __webpack_require__(2); + + var _range = __webpack_require__(1026); + + var _range2 = _interopRequireDefault(_range); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + + class HighlightLines extends _react.Component { + + constructor() { + super(); + this.highlightLineRange = this.highlightLineRange.bind(this); + } + + componentDidMount() { + if (!this.props.editor) { + return; + } + + this.highlightLineRange(); + } + + componentWillUpdate() { + this.clearHighlightRange(); + } + + componentDidUpdate() { + this.highlightLineRange(); + } + + componentWillUnmount() { + this.clearHighlightRange(); + } + + clearHighlightRange() { + var _props = this.props, + highlightedLineRange = _props.highlightedLineRange, + editor = _props.editor; + + + if (!editor) { + return; + } + + var codeMirror = editor.codeMirror; + + + if (!highlightedLineRange || !editor || !codeMirror) { + return; + } + + var start = highlightedLineRange.start, + end = highlightedLineRange.end; + + + (0, _range2.default)(start - 1, end).forEach(line => { + codeMirror.removeLineClass(line, "line", "highlight-lines"); + }); + } + + highlightLineRange() { + var _props2 = this.props, + highlightedLineRange = _props2.highlightedLineRange, + editor = _props2.editor; + + + if (!editor) { + return; + } + + var codeMirror = editor.codeMirror; + + + if (!highlightedLineRange || !codeMirror) { + return; + } + + var start = highlightedLineRange.start, + end = highlightedLineRange.end; + + + editor.alignLine(start); + + (0, _range2.default)(start - 1, end).forEach(line => { + codeMirror.addLineClass(line, "line", "highlight-lines"); + }); + } + + render() { + return null; + } + } + + HighlightLines.displayName = "HighlightLines"; + + exports.default = HighlightLines; + +/***/ }, +/* 1026 */ +/***/ function(module, exports, __webpack_require__) { + + var createRange = __webpack_require__(1027); + + /** + * Creates an array of numbers (positive and/or negative) progressing from + * `start` up to, but not including, `end`. A step of `-1` is used if a negative + * `start` is specified without an `end` or `step`. If `end` is not specified, + * it's set to `start` with `start` then set to `0`. + * + * **Note:** JavaScript follows the IEEE-754 standard for resolving + * floating-point values which can produce unexpected results. + * + * @static + * @since 0.1.0 + * @memberOf _ + * @category Util + * @param {number} [start=0] The start of the range. + * @param {number} end The end of the range. + * @param {number} [step=1] The value to increment or decrement by. + * @returns {Array} Returns the range of numbers. + * @see _.inRange, _.rangeRight + * @example + * + * _.range(4); + * // => [0, 1, 2, 3] + * + * _.range(-4); + * // => [0, -1, -2, -3] + * + * _.range(1, 5); + * // => [1, 2, 3, 4] + * + * _.range(0, 20, 5); + * // => [0, 5, 10, 15] + * + * _.range(0, -4, -1); + * // => [0, -1, -2, -3] + * + * _.range(1, 4, 0); + * // => [1, 1, 1] + * + * _.range(0); + * // => [] + */ + var range = createRange(); + + module.exports = range; + + +/***/ }, +/* 1027 */ +/***/ function(module, exports, __webpack_require__) { + + var baseRange = __webpack_require__(1028), + isIterateeCall = __webpack_require__(418), + toFinite = __webpack_require__(303); + + /** + * Creates a `_.range` or `_.rangeRight` function. + * + * @private + * @param {boolean} [fromRight] Specify iterating from right to left. + * @returns {Function} Returns the new range function. + */ + function createRange(fromRight) { + return function(start, end, step) { + if (step && typeof step != 'number' && isIterateeCall(start, end, step)) { + end = step = undefined; + } + // Ensure the sign of `-0` is preserved. + start = toFinite(start); + if (end === undefined) { + end = start; + start = 0; + } else { + end = toFinite(end); + } + step = step === undefined ? (start < end ? 1 : -1) : toFinite(step); + return baseRange(start, end, step, fromRight); + }; + } + + module.exports = createRange; + + +/***/ }, +/* 1028 */ +/***/ function(module, exports) { + + /* Built-in method references for those with the same name as other `lodash` methods. */ + var nativeCeil = Math.ceil, + nativeMax = Math.max; + + /** + * The base implementation of `_.range` and `_.rangeRight` which doesn't + * coerce arguments. + * + * @private + * @param {number} start The start of the range. + * @param {number} end The end of the range. + * @param {number} step The value to increment or decrement by. + * @param {boolean} [fromRight] Specify iterating from right to left. + * @returns {Array} Returns the range of numbers. + */ + function baseRange(start, end, step, fromRight) { + var index = -1, + length = nativeMax(nativeCeil((end - start) / (step || 1)), 0), + result = Array(length); + + while (length--) { + result[fromRight ? length : ++index] = start; + start += step; + } + return result; + } + + module.exports = baseRange; + + +/***/ }, +/* 1029 */ +/***/ function(module, exports, __webpack_require__) { + + "use strict"; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + var _react = __webpack_require__(2); + + var _classnames = __webpack_require__(175); + + var _classnames2 = _interopRequireDefault(_classnames); + + __webpack_require__(1030); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + + var BracketArrow = (_ref) => { + var orientation = _ref.orientation, + left = _ref.left, + top = _ref.top, + bottom = _ref.bottom; + + return _react.DOM.div({ + className: (0, _classnames2.default)("bracket-arrow", orientation || "up"), + style: { left, top, bottom } + }, ""); + }; + + BracketArrow.propTypes = { + orientation: _react.PropTypes.string, + left: _react.PropTypes.number, + top: _react.PropTypes.number, + bottom: _react.PropTypes.number + }; + + BracketArrow.displayName = "BracketArrow"; + + exports.default = BracketArrow; + +/***/ }, +/* 1030 */ +/***/ function(module, exports) { + + // removed by extract-text-webpack-plugin + +/***/ }, +/* 1031 */, +/* 1032 */ +/***/ function(module, exports, __webpack_require__) { + + "use strict"; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + exports.default = FrameMenu; + + var _devtoolsLaunchpad = __webpack_require__(131); + + var _clipboard = __webpack_require__(423); + + var _kebabCase = __webpack_require__(1034); + + function formatMenuElement(labelString, accesskeyString, click) { + var disabled = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false; + + var label = L10N.getStr(labelString); + var id = `node-menu-${(0, _kebabCase.kebabCase)(label)}`; + return { + id, + label, + accesskey: L10N.getStr(accesskeyString), + disabled, + click + }; + } + + + function copySourceElement(url) { + return formatMenuElement("copySourceUrl", "copySourceUrl.accesskey", () => (0, _clipboard.copyToTheClipboard)(url)); + } + + function copyStackTraceElement(copyStackTrace) { + return formatMenuElement("copyStackTrace", "copyStackTrace.accesskey", () => copyStackTrace()); + } + + function FrameMenu(frame, copyStackTrace, event) { + event.stopPropagation(); + event.preventDefault(); + + var menuOptions = []; + + var source = frame.source; + if (source) { + var copySourceUrl = copySourceElement(source.url); + menuOptions.push(copySourceUrl); + } + + var copyStackTraceItem = copyStackTraceElement(copyStackTrace); + + menuOptions.push(copyStackTraceItem); + + (0, _devtoolsLaunchpad.showMenu)(event, menuOptions); + } + +/***/ }, +/* 1033 */, +/* 1034 */ +/***/ function(module, exports, __webpack_require__) { + + var createCompounder = __webpack_require__(1035); + + /** + * Converts `string` to + * [kebab case](https://en.wikipedia.org/wiki/Letter_case#Special_case_styles). + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category String + * @param {string} [string=''] The string to convert. + * @returns {string} Returns the kebab cased string. + * @example + * + * _.kebabCase('Foo Bar'); + * // => 'foo-bar' + * + * _.kebabCase('fooBar'); + * // => 'foo-bar' + * + * _.kebabCase('__FOO_BAR__'); + * // => 'foo-bar' + */ + var kebabCase = createCompounder(function(result, word, index) { + return result + (index ? '-' : '') + word.toLowerCase(); + }); + + module.exports = kebabCase; + + +/***/ }, +/* 1035 */ +/***/ function(module, exports, __webpack_require__) { + + var arrayReduce = __webpack_require__(556), + deburr = __webpack_require__(1036), + words = __webpack_require__(1039); + + /** Used to compose unicode capture groups. */ + var rsApos = "['\u2019]"; + + /** Used to match apostrophes. */ + var reApos = RegExp(rsApos, 'g'); + + /** + * Creates a function like `_.camelCase`. + * + * @private + * @param {Function} callback The function to combine each word. + * @returns {Function} Returns the new compounder function. + */ + function createCompounder(callback) { + return function(string) { + return arrayReduce(words(deburr(string).replace(reApos, '')), callback, ''); + }; + } + + module.exports = createCompounder; + + +/***/ }, +/* 1036 */ +/***/ function(module, exports, __webpack_require__) { + + var deburrLetter = __webpack_require__(1037), + toString = __webpack_require__(108); + + /** Used to match Latin Unicode letters (excluding mathematical operators). */ + var reLatin = /[\xc0-\xd6\xd8-\xf6\xf8-\xff\u0100-\u017f]/g; + + /** Used to compose unicode character classes. */ + var rsComboMarksRange = '\\u0300-\\u036f', + reComboHalfMarksRange = '\\ufe20-\\ufe2f', + rsComboSymbolsRange = '\\u20d0-\\u20ff', + rsComboRange = rsComboMarksRange + reComboHalfMarksRange + rsComboSymbolsRange; + + /** Used to compose unicode capture groups. */ + var rsCombo = '[' + rsComboRange + ']'; + + /** + * Used to match [combining diacritical marks](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks) and + * [combining diacritical marks for symbols](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks_for_Symbols). + */ + var reComboMark = RegExp(rsCombo, 'g'); + + /** + * Deburrs `string` by converting + * [Latin-1 Supplement](https://en.wikipedia.org/wiki/Latin-1_Supplement_(Unicode_block)#Character_table) + * and [Latin Extended-A](https://en.wikipedia.org/wiki/Latin_Extended-A) + * letters to basic Latin letters and removing + * [combining diacritical marks](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks). + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category String + * @param {string} [string=''] The string to deburr. + * @returns {string} Returns the deburred string. + * @example + * + * _.deburr('déjà vu'); + * // => 'deja vu' + */ + function deburr(string) { + string = toString(string); + return string && string.replace(reLatin, deburrLetter).replace(reComboMark, ''); + } + + module.exports = deburr; + + +/***/ }, +/* 1037 */ +/***/ function(module, exports, __webpack_require__) { + + var basePropertyOf = __webpack_require__(1038); + + /** Used to map Latin Unicode letters to basic Latin letters. */ + var deburredLetters = { + // Latin-1 Supplement block. + '\xc0': 'A', '\xc1': 'A', '\xc2': 'A', '\xc3': 'A', '\xc4': 'A', '\xc5': 'A', + '\xe0': 'a', '\xe1': 'a', '\xe2': 'a', '\xe3': 'a', '\xe4': 'a', '\xe5': 'a', + '\xc7': 'C', '\xe7': 'c', + '\xd0': 'D', '\xf0': 'd', + '\xc8': 'E', '\xc9': 'E', '\xca': 'E', '\xcb': 'E', + '\xe8': 'e', '\xe9': 'e', '\xea': 'e', '\xeb': 'e', + '\xcc': 'I', '\xcd': 'I', '\xce': 'I', '\xcf': 'I', + '\xec': 'i', '\xed': 'i', '\xee': 'i', '\xef': 'i', + '\xd1': 'N', '\xf1': 'n', + '\xd2': 'O', '\xd3': 'O', '\xd4': 'O', '\xd5': 'O', '\xd6': 'O', '\xd8': 'O', + '\xf2': 'o', '\xf3': 'o', '\xf4': 'o', '\xf5': 'o', '\xf6': 'o', '\xf8': 'o', + '\xd9': 'U', '\xda': 'U', '\xdb': 'U', '\xdc': 'U', + '\xf9': 'u', '\xfa': 'u', '\xfb': 'u', '\xfc': 'u', + '\xdd': 'Y', '\xfd': 'y', '\xff': 'y', + '\xc6': 'Ae', '\xe6': 'ae', + '\xde': 'Th', '\xfe': 'th', + '\xdf': 'ss', + // Latin Extended-A block. + '\u0100': 'A', '\u0102': 'A', '\u0104': 'A', + '\u0101': 'a', '\u0103': 'a', '\u0105': 'a', + '\u0106': 'C', '\u0108': 'C', '\u010a': 'C', '\u010c': 'C', + '\u0107': 'c', '\u0109': 'c', '\u010b': 'c', '\u010d': 'c', + '\u010e': 'D', '\u0110': 'D', '\u010f': 'd', '\u0111': 'd', + '\u0112': 'E', '\u0114': 'E', '\u0116': 'E', '\u0118': 'E', '\u011a': 'E', + '\u0113': 'e', '\u0115': 'e', '\u0117': 'e', '\u0119': 'e', '\u011b': 'e', + '\u011c': 'G', '\u011e': 'G', '\u0120': 'G', '\u0122': 'G', + '\u011d': 'g', '\u011f': 'g', '\u0121': 'g', '\u0123': 'g', + '\u0124': 'H', '\u0126': 'H', '\u0125': 'h', '\u0127': 'h', + '\u0128': 'I', '\u012a': 'I', '\u012c': 'I', '\u012e': 'I', '\u0130': 'I', + '\u0129': 'i', '\u012b': 'i', '\u012d': 'i', '\u012f': 'i', '\u0131': 'i', + '\u0134': 'J', '\u0135': 'j', + '\u0136': 'K', '\u0137': 'k', '\u0138': 'k', + '\u0139': 'L', '\u013b': 'L', '\u013d': 'L', '\u013f': 'L', '\u0141': 'L', + '\u013a': 'l', '\u013c': 'l', '\u013e': 'l', '\u0140': 'l', '\u0142': 'l', + '\u0143': 'N', '\u0145': 'N', '\u0147': 'N', '\u014a': 'N', + '\u0144': 'n', '\u0146': 'n', '\u0148': 'n', '\u014b': 'n', + '\u014c': 'O', '\u014e': 'O', '\u0150': 'O', + '\u014d': 'o', '\u014f': 'o', '\u0151': 'o', + '\u0154': 'R', '\u0156': 'R', '\u0158': 'R', + '\u0155': 'r', '\u0157': 'r', '\u0159': 'r', + '\u015a': 'S', '\u015c': 'S', '\u015e': 'S', '\u0160': 'S', + '\u015b': 's', '\u015d': 's', '\u015f': 's', '\u0161': 's', + '\u0162': 'T', '\u0164': 'T', '\u0166': 'T', + '\u0163': 't', '\u0165': 't', '\u0167': 't', + '\u0168': 'U', '\u016a': 'U', '\u016c': 'U', '\u016e': 'U', '\u0170': 'U', '\u0172': 'U', + '\u0169': 'u', '\u016b': 'u', '\u016d': 'u', '\u016f': 'u', '\u0171': 'u', '\u0173': 'u', + '\u0174': 'W', '\u0175': 'w', + '\u0176': 'Y', '\u0177': 'y', '\u0178': 'Y', + '\u0179': 'Z', '\u017b': 'Z', '\u017d': 'Z', + '\u017a': 'z', '\u017c': 'z', '\u017e': 'z', + '\u0132': 'IJ', '\u0133': 'ij', + '\u0152': 'Oe', '\u0153': 'oe', + '\u0149': "'n", '\u017f': 's' + }; + + /** + * Used by `_.deburr` to convert Latin-1 Supplement and Latin Extended-A + * letters to basic Latin letters. + * + * @private + * @param {string} letter The matched letter to deburr. + * @returns {string} Returns the deburred letter. + */ + var deburrLetter = basePropertyOf(deburredLetters); + + module.exports = deburrLetter; + + +/***/ }, +/* 1038 */ +/***/ function(module, exports) { + + /** + * The base implementation of `_.propertyOf` without support for deep paths. + * + * @private + * @param {Object} object The object to query. + * @returns {Function} Returns the new accessor function. + */ + function basePropertyOf(object) { + return function(key) { + return object == null ? undefined : object[key]; + }; + } + + module.exports = basePropertyOf; + + +/***/ }, +/* 1039 */ +/***/ function(module, exports, __webpack_require__) { + + var asciiWords = __webpack_require__(1040), + hasUnicodeWord = __webpack_require__(1041), + toString = __webpack_require__(108), + unicodeWords = __webpack_require__(1042); + + /** + * Splits `string` into an array of its words. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category String + * @param {string} [string=''] The string to inspect. + * @param {RegExp|string} [pattern] The pattern to match words. + * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. + * @returns {Array} Returns the words of `string`. + * @example + * + * _.words('fred, barney, & pebbles'); + * // => ['fred', 'barney', 'pebbles'] + * + * _.words('fred, barney, & pebbles', /[^, ]+/g); + * // => ['fred', 'barney', '&', 'pebbles'] + */ + function words(string, pattern, guard) { + string = toString(string); + pattern = guard ? undefined : pattern; + + if (pattern === undefined) { + return hasUnicodeWord(string) ? unicodeWords(string) : asciiWords(string); + } + return string.match(pattern) || []; + } + + module.exports = words; + + +/***/ }, +/* 1040 */ +/***/ function(module, exports) { + + /** Used to match words composed of alphanumeric characters. */ + var reAsciiWord = /[^\x00-\x2f\x3a-\x40\x5b-\x60\x7b-\x7f]+/g; + + /** + * Splits an ASCII `string` into an array of its words. + * + * @private + * @param {string} The string to inspect. + * @returns {Array} Returns the words of `string`. + */ + function asciiWords(string) { + return string.match(reAsciiWord) || []; + } + + module.exports = asciiWords; + + +/***/ }, +/* 1041 */ +/***/ function(module, exports) { + + /** Used to detect strings that need a more robust regexp to match words. */ + var reHasUnicodeWord = /[a-z][A-Z]|[A-Z]{2,}[a-z]|[0-9][a-zA-Z]|[a-zA-Z][0-9]|[^a-zA-Z0-9 ]/; + + /** + * Checks if `string` contains a word composed of Unicode symbols. + * + * @private + * @param {string} string The string to inspect. + * @returns {boolean} Returns `true` if a word is found, else `false`. + */ + function hasUnicodeWord(string) { + return reHasUnicodeWord.test(string); + } + + module.exports = hasUnicodeWord; + + +/***/ }, +/* 1042 */ +/***/ function(module, exports) { + + /** Used to compose unicode character classes. */ + var rsAstralRange = '\\ud800-\\udfff', + rsComboMarksRange = '\\u0300-\\u036f', + reComboHalfMarksRange = '\\ufe20-\\ufe2f', + rsComboSymbolsRange = '\\u20d0-\\u20ff', + rsComboRange = rsComboMarksRange + reComboHalfMarksRange + rsComboSymbolsRange, + rsDingbatRange = '\\u2700-\\u27bf', + rsLowerRange = 'a-z\\xdf-\\xf6\\xf8-\\xff', + rsMathOpRange = '\\xac\\xb1\\xd7\\xf7', + rsNonCharRange = '\\x00-\\x2f\\x3a-\\x40\\x5b-\\x60\\x7b-\\xbf', + rsPunctuationRange = '\\u2000-\\u206f', + rsSpaceRange = ' \\t\\x0b\\f\\xa0\\ufeff\\n\\r\\u2028\\u2029\\u1680\\u180e\\u2000\\u2001\\u2002\\u2003\\u2004\\u2005\\u2006\\u2007\\u2008\\u2009\\u200a\\u202f\\u205f\\u3000', + rsUpperRange = 'A-Z\\xc0-\\xd6\\xd8-\\xde', + rsVarRange = '\\ufe0e\\ufe0f', + rsBreakRange = rsMathOpRange + rsNonCharRange + rsPunctuationRange + rsSpaceRange; + + /** Used to compose unicode capture groups. */ + var rsApos = "['\u2019]", + rsBreak = '[' + rsBreakRange + ']', + rsCombo = '[' + rsComboRange + ']', + rsDigits = '\\d+', + rsDingbat = '[' + rsDingbatRange + ']', + rsLower = '[' + rsLowerRange + ']', + rsMisc = '[^' + rsAstralRange + rsBreakRange + rsDigits + rsDingbatRange + rsLowerRange + rsUpperRange + ']', + rsFitz = '\\ud83c[\\udffb-\\udfff]', + rsModifier = '(?:' + rsCombo + '|' + rsFitz + ')', + rsNonAstral = '[^' + rsAstralRange + ']', + rsRegional = '(?:\\ud83c[\\udde6-\\uddff]){2}', + rsSurrPair = '[\\ud800-\\udbff][\\udc00-\\udfff]', + rsUpper = '[' + rsUpperRange + ']', + rsZWJ = '\\u200d'; + + /** Used to compose unicode regexes. */ + var rsMiscLower = '(?:' + rsLower + '|' + rsMisc + ')', + rsMiscUpper = '(?:' + rsUpper + '|' + rsMisc + ')', + rsOptContrLower = '(?:' + rsApos + '(?:d|ll|m|re|s|t|ve))?', + rsOptContrUpper = '(?:' + rsApos + '(?:D|LL|M|RE|S|T|VE))?', + reOptMod = rsModifier + '?', + rsOptVar = '[' + rsVarRange + ']?', + rsOptJoin = '(?:' + rsZWJ + '(?:' + [rsNonAstral, rsRegional, rsSurrPair].join('|') + ')' + rsOptVar + reOptMod + ')*', + rsOrdLower = '\\d*(?:(?:1st|2nd|3rd|(?![123])\\dth)\\b)', + rsOrdUpper = '\\d*(?:(?:1ST|2ND|3RD|(?![123])\\dTH)\\b)', + rsSeq = rsOptVar + reOptMod + rsOptJoin, + rsEmoji = '(?:' + [rsDingbat, rsRegional, rsSurrPair].join('|') + ')' + rsSeq; + + /** Used to match complex or compound words. */ + var reUnicodeWord = RegExp([ + rsUpper + '?' + rsLower + '+' + rsOptContrLower + '(?=' + [rsBreak, rsUpper, '$'].join('|') + ')', + rsMiscUpper + '+' + rsOptContrUpper + '(?=' + [rsBreak, rsUpper + rsMiscLower, '$'].join('|') + ')', + rsUpper + '?' + rsMiscLower + '+' + rsOptContrLower, + rsUpper + '+' + rsOptContrUpper, + rsOrdUpper, + rsOrdLower, + rsDigits, + rsEmoji + ].join('|'), 'g'); + + /** + * Splits a Unicode `string` into an array of its words. + * + * @private + * @param {string} The string to inspect. + * @returns {Array} Returns the words of `string`. + */ + function unicodeWords(string) { + return string.match(reUnicodeWord) || []; + } + + module.exports = unicodeWords; + + /***/ } /******/ ]) }); diff --git a/devtools/client/debugger/new/parser-worker.js b/devtools/client/debugger/new/parser-worker.js index ee00d44cbd6d5..eaeff2bace042 100644 --- a/devtools/client/debugger/new/parser-worker.js +++ b/devtools/client/debugger/new/parser-worker.js @@ -5368,7 +5368,7 @@ return /******/ (function(modules) { // webpackBootstrap this.potentialArrowAt = -1; - this.inMethod = this.inFunction = this.inGenerator = this.inAsync = this.inPropertyName = this.inType = this.noAnonFunctionType = false; + this.inMethod = this.inFunction = this.inGenerator = this.inAsync = this.inPropertyName = this.inType = this.inClassProperty = this.noAnonFunctionType = false; this.labels = []; @@ -6080,25 +6080,29 @@ return /******/ (function(modules) { // webpackBootstrap Tokenizer.prototype.readNumber = function readNumber(startsWithDot) { var start = this.state.pos; - var octal = this.input.charCodeAt(this.state.pos) === 48; + var octal = this.input.charCodeAt(start) === 48; // '0' var isFloat = false; if (!startsWithDot && this.readInt(10) === null) this.raise(start, "Invalid number"); + if (octal && this.state.pos == start + 1) octal = false; // number === 0 + var next = this.input.charCodeAt(this.state.pos); - if (next === 46) { + if (next === 46 && !octal) { // '.' ++this.state.pos; this.readInt(10); isFloat = true; next = this.input.charCodeAt(this.state.pos); } - if (next === 69 || next === 101) { + + if ((next === 69 || next === 101) && !octal) { // 'eE' next = this.input.charCodeAt(++this.state.pos); if (next === 43 || next === 45) ++this.state.pos; // '+-' if (this.readInt(10) === null) this.raise(start, "Invalid number"); isFloat = true; } + if (isIdentifierStart(this.fullCharCodeAtPos())) this.raise(this.state.pos, "Identifier directly after number"); var str = this.input.slice(start, this.state.pos); @@ -6107,8 +6111,10 @@ return /******/ (function(modules) { // webpackBootstrap val = parseFloat(str); } else if (!octal || str.length === 1) { val = parseInt(str, 10); - } else if (/[89]/.test(str) || this.state.strict) { + } else if (this.state.strict) { this.raise(start, "Invalid number"); + } else if (/[89]/.test(str)) { + val = parseInt(str, 10); } else { val = parseInt(str, 8); } @@ -7407,6 +7413,7 @@ return /******/ (function(modules) { // webpackBootstrap }; pp$1.parseClassProperty = function (node) { + this.state.inClassProperty = true; if (this.match(types.eq)) { if (!this.hasPlugin("classProperties")) this.unexpected(); this.next(); @@ -7415,6 +7422,7 @@ return /******/ (function(modules) { // webpackBootstrap node.value = null; } this.semicolon(); + this.state.inClassProperty = false; return this.finishNode(node, "ClassProperty"); }; @@ -8446,7 +8454,7 @@ return /******/ (function(modules) { // webpackBootstrap switch (this.state.type) { case types._super: - if (!this.state.inMethod && !this.options.allowSuperOutsideMethod) { + if (!this.state.inMethod && !this.state.inClassProperty && !this.options.allowSuperOutsideMethod) { this.raise(this.state.start, "'super' outside of function or class"); } @@ -8955,8 +8963,9 @@ return /******/ (function(modules) { // webpackBootstrap } if (!prop.computed && prop.key.type === "Identifier") { + this.checkReservedWord(prop.key.name, prop.key.start, true, true); + if (isPattern) { - this.checkReservedWord(prop.key.name, prop.key.start, true, true); prop.value = this.parseMaybeDefault(startPos, startLoc, prop.key.__clone()); } else if (this.match(types.eq) && refShorthandDefaultPos) { if (!refShorthandDefaultPos.start) { @@ -10199,7 +10208,7 @@ return /******/ (function(modules) { // webpackBootstrap } else { if (this.match(types.ellipsis)) { if (!allowSpread) { - this.unexpected(null, "Spread operator cannnot appear in class or interface definitions"); + this.unexpected(null, "Spread operator cannot appear in class or interface definitions"); } if (variance) { this.unexpected(variance.start, "Spread properties cannot have variance"); @@ -10885,6 +10894,12 @@ return /******/ (function(modules) { // webpackBootstrap }; }); + instance.extend("isNonstaticConstructor", function (inner) { + return function (method) { + return !this.match(types.colon) && inner.call(this, method); + }; + }); + // parse type parameters for class methods instance.extend("parseClassMethod", function (inner) { return function (classBody, method) { @@ -11141,6 +11156,12 @@ return /******/ (function(modules) { // webpackBootstrap } catch (err) { if (err instanceof SyntaxError) { this.state = state; + + // Remove `tc.j_expr` and `tc.j_oTag` from context added + // by parsing `jsxTagStart` to stop the JSX plugin from + // messing with the tokens + this.state.context.length -= 2; + jsxError = err; } else { // istanbul ignore next: no such error is expected @@ -11149,9 +11170,6 @@ return /******/ (function(modules) { // webpackBootstrap } } - // Need to push something onto the context to stop - // the JSX plugin from messing with the tokens - this.state.context.push(types$1.parenExpression); if (jsxError != null || this.isRelational("<")) { var arrowExpression = void 0; var typeParameters = void 0; @@ -11174,7 +11192,6 @@ return /******/ (function(modules) { // webpackBootstrap this.raise(typeParameters.start, "Expected an arrow function after this type parameter declaration"); } } - this.state.context.pop(); return inner.apply(this, args); }; @@ -19540,7 +19557,7 @@ return /******/ (function(modules) { // webpackBootstrap exports.disable = disable; exports.enable = enable; exports.enabled = enabled; - exports.humanize = __webpack_require__(1016); + exports.humanize = __webpack_require__(1024); /** * The currently active debug mode names, and names to skip. @@ -28947,41 +28964,63 @@ return /******/ (function(modules) { // webpackBootstrap exports.getVariablesInScope = getVariablesInScope; exports.isExpressionInScope = isExpressionInScope; - function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } } + var _babylon = __webpack_require__(435); - var babylon = __webpack_require__(435); - var traverse = __webpack_require__(436).default; - var t = __webpack_require__(493); + var babylon = _interopRequireWildcard(_babylon); + + var _babelTraverse = __webpack_require__(436); + + var _babelTraverse2 = _interopRequireDefault(_babelTraverse); + + var _babelTypes = __webpack_require__(493); + + var t = _interopRequireWildcard(_babelTypes); + + var _devtoolsConfig = __webpack_require__(828); + + var _toPairs = __webpack_require__(195); + + var _toPairs2 = _interopRequireDefault(_toPairs); + + var _isEmpty = __webpack_require__(963); + + var _isEmpty2 = _interopRequireDefault(_isEmpty); + + var _uniq = __webpack_require__(561); + + var _uniq2 = _interopRequireDefault(_uniq); - var _require = __webpack_require__(828), - isDevelopment = _require.isDevelopment; + var _parseScriptTags = __webpack_require__(1023); - var toPairs = __webpack_require__(195); - var isEmpty = __webpack_require__(963); - var uniq = __webpack_require__(561); + var _parseScriptTags2 = _interopRequireDefault(_parseScriptTags); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + + function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } + + function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } } var ASTs = new Map(); var symbolDeclarations = new Map(); - function _parse(code) { - return babylon.parse(code, { + function _parse(code, opts) { + return babylon.parse(code, Object.assign({}, opts, { sourceType: "module", - plugins: ["jsx", "flow"] - }); + })); } - function parse(text) { + function parse(text, opts) { var ast = void 0; if (!text) { return; } try { - ast = _parse(text); + ast = _parse(text, opts); } catch (error) { - if (isDevelopment()) { + if ((0, _devtoolsConfig.isDevelopment)()) { console.warn("parse failed", text); } @@ -28997,7 +29036,19 @@ return /******/ (function(modules) { // webpackBootstrap } var ast = {}; - if (sourceText.contentType == "text/javascript") { + if (sourceText.contentType == "text/html") { + // Custom parser for parse-script-tags that adapts its input structure to + // our parser's signature + var parser = (_ref) => { + var source = _ref.source, + line = _ref.line; + + return parse(source, { + startLine: line + }); + }; + ast = (0, _parseScriptTags2.default)(sourceText.text, parser) || {}; + } else if (sourceText.contentType == "text/javascript") { ast = parse(sourceText.text); } @@ -29046,32 +29097,22 @@ return /******/ (function(modules) { // webpackBootstrap return t.isFunction(path) || t.isArrowFunctionExpression(path) || t.isObjectMethod(path) || t.isClassMethod(path); } - function formatSymbol(symbol) { - return { - id: `${symbol.name}:${symbol.location.start.line}`, - title: symbol.name, - subtitle: `:${symbol.location.start.line}`, - value: symbol.name, - location: symbol.location - }; - } - function getVariableNames(path) { if (t.isObjectProperty(path) && !isFunction(path.node.value)) { - return [formatSymbol({ + return [{ name: path.node.key.name, location: path.node.loc - })]; + }]; } if (!path.node.declarations) { - return path.node.params.map(dec => formatSymbol({ + return path.node.params.map(dec => ({ name: dec.name, location: dec.loc })); } - return path.node.declarations.map(dec => formatSymbol({ + return path.node.declarations.map(dec => ({ name: dec.id.name, location: dec.loc })); @@ -29101,10 +29142,10 @@ return /******/ (function(modules) { // webpackBootstrap var bindings = scope.bindings; - return toPairs(bindings).map((_ref) => { - var _ref2 = _slicedToArray(_ref, 2), - name = _ref2[0], - binding = _ref2[1]; + return (0, _toPairs2.default)(bindings).map((_ref2) => { + var _ref3 = _slicedToArray(_ref2, 2), + name = _ref3[0], + binding = _ref3[1]; return { name, @@ -29127,9 +29168,9 @@ return /******/ (function(modules) { // webpackBootstrap * helps find member expressions on one line and function scopes that are * often many lines */ - function nodeContainsLocation(_ref3) { - var node = _ref3.node, - location = _ref3.location; + function nodeContainsLocation(_ref4) { + var node = _ref4.node, + location = _ref4.location; var _node$loc = node.loc, start = _node$loc.start, end = _node$loc.end; @@ -29171,11 +29212,11 @@ return /******/ (function(modules) { // webpackBootstrap var symbols = { functions: [], variables: [] }; - if (isEmpty(ast)) { + if ((0, _isEmpty2.default)(ast)) { return symbols; } - traverse(ast, { + (0, _babelTraverse2.default)(ast, { enter(path) { if (isVariable(path)) { var _symbols$variables; @@ -29184,17 +29225,17 @@ return /******/ (function(modules) { // webpackBootstrap } if (isFunction(path)) { - symbols.functions.push(formatSymbol({ + symbols.functions.push({ name: getFunctionName(path), location: path.node.loc - })); + }); } if (t.isClassDeclaration(path)) { - symbols.variables.push(formatSymbol({ + symbols.variables.push({ name: path.node.id.name, location: path.node.loc - })); + }); } } }); @@ -29205,12 +29246,12 @@ return /******/ (function(modules) { // webpackBootstrap function getClosestMemberExpression(source, token, location) { var ast = getAst(source); - if (isEmpty(ast)) { + if ((0, _isEmpty2.default)(ast)) { return null; } var expression = null; - traverse(ast, { + (0, _babelTraverse2.default)(ast, { enter(path) { var node = path.node; @@ -29263,13 +29304,13 @@ return /******/ (function(modules) { // webpackBootstrap function getClosestScope(source, location) { var ast = getAst(source); - if (isEmpty(ast)) { + if ((0, _isEmpty2.default)(ast)) { return null; } var closestPath = null; - traverse(ast, { + (0, _babelTraverse2.default)(ast, { enter(path) { if (isLexicalScope(path) && nodeContainsLocation({ node: path.node, location })) { closestPath = path; @@ -29286,13 +29327,13 @@ return /******/ (function(modules) { // webpackBootstrap function getClosestPath(source, location) { var ast = getAst(source); - if (isEmpty(ast)) { + if ((0, _isEmpty2.default)(ast)) { return null; } var closestPath = null; - traverse(ast, { + (0, _babelTraverse2.default)(ast, { enter(path) { if (nodeContainsLocation({ node: path.node, location })) { closestPath = path; @@ -29308,12 +29349,12 @@ return /******/ (function(modules) { // webpackBootstrap } function getVariablesInScope(scope) { - var _ref4; + var _ref5; var scopes = getScopeChain(scope); var scopeVars = scopes.map(getScopeVariables); - var vars = (_ref4 = [{ name: "this" }, { name: "arguments" }]).concat.apply(_ref4, _toConsumableArray(scopeVars)).map(variable => variable.name); - return uniq(vars); + var vars = (_ref5 = [{ name: "this" }, { name: "arguments" }]).concat.apply(_ref5, _toConsumableArray(scopeVars)).map(variable => variable.name); + return (0, _uniq2.default)(vars); } function isExpressionInScope(expression, scope) { @@ -29462,7 +29503,159 @@ return /******/ (function(modules) { // webpackBootstrap /* 1013 */, /* 1014 */, /* 1015 */, -/* 1016 */ +/* 1016 */, +/* 1017 */, +/* 1018 */, +/* 1019 */, +/* 1020 */, +/* 1021 */, +/* 1022 */, +/* 1023 */ +/***/ function(module, exports, __webpack_require__) { + + "use strict"; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } } + + var babylon = __webpack_require__(435); + var types = __webpack_require__(493); + + var startScript = /]*>/im; + var endScript = /<\/script\s*>/im; + + function getCandidateScriptLocations(source, index) { + var i = index || 0; + var str = source.substring(i); + + var startMatch = startScript.exec(str); + if (startMatch) { + var startsAt = startMatch.index + startMatch[0].length; + var afterStart = str.substring(startsAt); + var endMatch = endScript.exec(afterStart); + if (endMatch) { + var locLength = endMatch.index; + var locIndex = i + startsAt; + + return [adjustForLineAndColumn(source, { + index: locIndex, + length: locLength, + source: source.substring(locIndex, locIndex + locLength) + })].concat(_toConsumableArray(getCandidateScriptLocations(source, locIndex + locLength + endMatch[0].length))); + } + } + + return []; + } + + function parseScript(_ref) { + var source = _ref.source, + line = _ref.line; + + // remove empty or only whitespace scripts + if (source.length === 0 || /^\s+$/.test(source)) { + return null; + } + + try { + return babylon.parse(source, { + sourceType: "script", + startLine: line + }); + } catch (e) { + return null; + } + } + + function parseScripts(locations) { + var parser = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : parseScript; + + return locations.map(parser); + } + + function generateWhitespace(length) { + return Array.from(new Array(length + 1)).join(" "); + } + + function calcLineAndColumn(source, index) { + var lines = source.substring(0, index).replace(/\r\l?/, "\n").split(/\n/); + var line = lines.length; + var column = lines.pop().length + 1; + + return { + column: column, + line: line + }; + } + + function adjustForLineAndColumn(fullSource, location) { + var _calcLineAndColumn = calcLineAndColumn(fullSource, location.index), + column = _calcLineAndColumn.column, + line = _calcLineAndColumn.line; + + return Object.assign({}, location, { + line: line, + column: column, + // prepend whitespace for scripts that do not start on the first column + source: generateWhitespace(column) + location.source + }); + } + + function parseScriptTags(source, parser) { + var scripts = parseScripts(getCandidateScriptLocations(source), parser).filter(types.isFile).reduce(function (main, script) { + return { + statements: main.statements.concat(script.program.body), + comments: main.comments.concat(script.comments), + tokens: main.tokens.concat(script.tokens) + }; + }, { + statements: [], + comments: [], + tokens: [] + }); + + var program = types.program(scripts.statements); + var file = types.file(program, scripts.comments, scripts.tokens); + + var end = calcLineAndColumn(source, source.length); + file.start = program.start = 0; + file.end = program.end = source.length; + file.loc = program.loc = { + start: { + line: 1, + column: 0 + }, + end: end + }; + + return file; + } + + function extractScriptTags(source) { + return parseScripts(getCandidateScriptLocations(source), function (loc) { + var ast = parseScript(loc); + + if (ast) { + return loc; + } + + return null; + }).filter(types.isFile); + } + + exports.default = parseScriptTags; + exports.extractScriptTags = extractScriptTags; + exports.generateWhitespace = generateWhitespace; + exports.getCandidateScriptLocations = getCandidateScriptLocations; + exports.parseScript = parseScript; + exports.parseScripts = parseScripts; + exports.parseScriptTags = parseScriptTags; + +/***/ }, +/* 1024 */ /***/ function(module, exports) { /** diff --git a/devtools/client/debugger/new/test/mochitest/browser_dbg-expressions.js b/devtools/client/debugger/new/test/mochitest/browser_dbg-expressions.js index 33a173053a6a1..5b9e77e99530e 100644 --- a/devtools/client/debugger/new/test/mochitest/browser_dbg-expressions.js +++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-expressions.js @@ -47,7 +47,7 @@ add_task(function*() { yield addExpression(dbg, "f"); is(getLabel(dbg, 1), "f"); - is(getValue(dbg, 1), "ReferenceError"); + is(getValue(dbg, 1), "(unavailable)"); yield editExpression(dbg, "oo"); is(getLabel(dbg, 1), "foo()"); diff --git a/devtools/client/debugger/new/test/mochitest/browser_dbg-scopes-mutations.js b/devtools/client/debugger/new/test/mochitest/browser_dbg-scopes-mutations.js index d6c7918f6d966..f85408881687c 100644 --- a/devtools/client/debugger/new/test/mochitest/browser_dbg-scopes-mutations.js +++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-scopes-mutations.js @@ -83,38 +83,13 @@ add_task(async function() { await onPaused; is( - getScopeNodeLabel(dbg, 6), - "lastName", - 'The sixth element in the scope panel is still "lastName"' - ); - is( - getScopeNodeValue(dbg, 6), - '"Doe"', - 'The "lastName" property is still "Doe", but it should be "Pierce"' + - "since it was changed in the script." - ); - - onPaused = waitForPaused(dbg); - await resume(dbg); - await onPaused; - is( - getScopeNodeLabel(dbg, 6), - "lastName", - 'The sixth element in the scope panel is still "lastName"' - ); - is( - getScopeNodeLabel(dbg, 7), - "__proto__", - 'The seventh element in the scope panel is still "__proto__", ' + - 'but it should be now "timezone", since it was added to the "sarah" object ' + - "in the script" + getScopeNodeLabel(dbg, 2), + "", + 'The second element in the scope panel is ""' ); is( - getScopeNodeValue(dbg, 7), - "Object", - 'The seventh element in the scope panel has the value "Object", ' + - 'but it should be "PST"' + getScopeNodeLabel(dbg, 3), + "phonebook", + 'The third element in the scope panel is "phonebook"' ); - - await resume(dbg); }); diff --git a/devtools/client/debugger/new/test/mochitest/browser_dbg-scopes.js b/devtools/client/debugger/new/test/mochitest/browser_dbg-scopes.js index 068309517d31a..6f321d5ef3e5f 100644 --- a/devtools/client/debugger/new/test/mochitest/browser_dbg-scopes.js +++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-scopes.js @@ -9,7 +9,7 @@ function getLabel(dbg, index) { return findElement(dbg, "scopeNode", index).innerText; } -add_task(function* () { +add_task(function*() { const dbg = yield initDebugger("doc-script-switching.html"); toggleScopes(dbg); @@ -23,9 +23,9 @@ add_task(function* () { toggleNode(dbg, 4); yield waitForDispatch(dbg, "LOAD_OBJECT_PROPERTIES"); - is(getLabel(dbg, 5), "prototype"); + is(getLabel(dbg, 5), "length"); yield stepOver(dbg); is(getLabel(dbg, 4), "foo()"); - is(getLabel(dbg, 5), "prototype"); + is(getLabel(dbg, 5), "Window"); }); diff --git a/devtools/client/debugger/new/test/mochitest/browser_dbg-sources.js b/devtools/client/debugger/new/test/mochitest/browser_dbg-sources.js index 39b6ed6c82000..ed70bd86f10d1 100644 --- a/devtools/client/debugger/new/test/mochitest/browser_dbg-sources.js +++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-sources.js @@ -11,11 +11,11 @@ function* waitForSourceCount(dbg, i) { }); } -add_task(function* () { +add_task(function*() { const dbg = yield initDebugger("doc-sources.html"); const { selectors: { getSelectedSource }, getState } = dbg; - yield waitForSources(dbg, "simple1"); + yield waitForSources(dbg, "simple1", "simple2", "nested-source", "long.js"); // Expand nodes and make sure more sources appear. is(findAllElements(dbg, "sourceNodes").length, 2); @@ -27,15 +27,21 @@ add_task(function* () { is(findAllElements(dbg, "sourceNodes").length, 8); // Select a source. - ok(!findElementWithSelector(dbg, ".sources-list .focused"), - "Source is not focused"); + ok( + !findElementWithSelector(dbg, ".sources-list .focused"), + "Source is not focused" + ); const selected = waitForDispatch(dbg, "SELECT_SOURCE"); clickElement(dbg, "sourceNode", 4); yield selected; - ok(findElementWithSelector(dbg, ".sources-list .focused"), - "Source is focused"); - ok(getSelectedSource(getState()).get("url").includes("nested-source.js"), - "The right source is selected"); + ok( + findElementWithSelector(dbg, ".sources-list .focused"), + "Source is focused" + ); + ok( + getSelectedSource(getState()).get("url").includes("nested-source.js"), + "The right source is selected" + ); // Make sure new sources appear in the list. ContentTask.spawn(gBrowser.selectedBrowser, null, function() { @@ -45,16 +51,20 @@ add_task(function* () { }); yield waitForSourceCount(dbg, 9); - is(findElement(dbg, "sourceNode", 7).textContent, - "math.min.js", - "The dynamic script exists"); + is( + findElement(dbg, "sourceNode", 7).textContent, + "math.min.js", + "The dynamic script exists" + ); // Make sure named eval sources appear in the list. ContentTask.spawn(gBrowser.selectedBrowser, null, function() { content.eval("window.evaledFunc = function() {} //# sourceURL=evaled.js"); }); yield waitForSourceCount(dbg, 11); - is(findElement(dbg, "sourceNode", 2).textContent, - "evaled.js", - "The eval script exists"); + is( + findElement(dbg, "sourceNode", 2).textContent, + "evaled.js", + "The eval script exists" + ); }); diff --git a/devtools/client/debugger/new/test/mochitest/head.js b/devtools/client/debugger/new/test/mochitest/head.js index 22548da4c738f..ea15d163231a1 100644 --- a/devtools/client/debugger/new/test/mochitest/head.js +++ b/devtools/client/debugger/new/test/mochitest/head.js @@ -176,8 +176,7 @@ function waitForSources(dbg, ...sources) { sources.map(url => { function sourceExists(state) { return getSources(state).some(s => { - let u = s.get("url"); - return u && u.includes(url); + return (s.get("url") || "").includes(url); }); } @@ -214,9 +213,8 @@ function assertPausedLocation(dbg, source, line) { is(location.get("line"), line); // Check the debug line - let cm = dbg.win.document.querySelector(".CodeMirror").CodeMirror; ok( - cm.lineInfo(line - 1).wrapClass.includes("debug-line"), + getCM(dbg).lineInfo(line - 1).wrapClass.includes("debug-line"), "Line is highlighted as paused" ); } @@ -245,7 +243,7 @@ function assertHighlightLocation(dbg, source, line) { "Highlighted line is visible" ); ok( - dbg.win.cm.lineInfo(line - 1).wrapClass.includes("highlight-line"), + getCM(dbg).lineInfo(line - 1).wrapClass.includes("highlight-line"), "Line is highlighted" ); } @@ -359,10 +357,7 @@ function findSource(dbg, url) { } const sources = dbg.selectors.getSources(dbg.getState()); - const source = sources.find(s => { - let u = s.get("url"); - return u && u.includes(url); - }); + const source = sources.find(s => (s.get("url") || "").includes(url)); if (!source) { throw new Error("Unable to find source: " + url); @@ -623,9 +618,12 @@ function isVisibleWithin(outerEl, innerEl) { const selectors = { callStackHeader: ".call-stack-pane ._header", callStackBody: ".call-stack-pane .pane", - expressionNode: i => `.expressions-list .tree-node:nth-child(${i}) .object-label`, - expressionValue: i => `.expressions-list .tree-node:nth-child(${i}) .object-value`, - expressionClose: i => `.expressions-list .expression-container:nth-child(${i}) .close`, + expressionNode: i => + `.expressions-list .tree-node:nth-child(${i}) .object-label`, + expressionValue: i => + `.expressions-list .tree-node:nth-child(${i}) .object-value`, + expressionClose: i => + `.expressions-list .expression-container:nth-child(${i}) .close`, expressionNodes: ".expressions-list .tree-node", scopesHeader: ".scopes-pane ._header", breakpointItem: i => `.breakpoints-list .breakpoint:nth-child(${i})`, @@ -746,3 +744,8 @@ function toggleCallStack(dbg) { function toggleScopes(dbg) { return findElement(dbg, "scopesHeader").click(); } + +function getCM(dbg) { + const el = dbg.win.document.querySelector(".CodeMirror"); + return el.CodeMirror; +} diff --git a/devtools/client/framework/toolbox-process-window.js b/devtools/client/framework/toolbox-process-window.js index 0b5ba60b85953..472fdcecbd95b 100644 --- a/devtools/client/framework/toolbox-process-window.js +++ b/devtools/client/framework/toolbox-process-window.js @@ -157,7 +157,7 @@ function bindToolboxHandlers() { } function setupThreadListeners(panel) { - updateBadgeText(panel._selectors().getPause(panel._getState())); + updateBadgeText(panel._selectors.getPause(panel._getState())); let onPaused = updateBadgeText.bind(null, true); let onResumed = updateBadgeText.bind(null, false); diff --git a/devtools/client/locales/en-US/debugger.properties b/devtools/client/locales/en-US/debugger.properties index ef515810a2b87..5654de0366af2 100644 --- a/devtools/client/locales/en-US/debugger.properties +++ b/devtools/client/locales/en-US/debugger.properties @@ -128,25 +128,33 @@ otherEvents=Other # source. blackboxCheckboxTooltip2=Toggle blackboxing -# LOCALIZATION NOTE (sources.search.key): Key shortcut to open the search for +# LOCALIZATION NOTE (sources.search.key2): Key shortcut to open the search for # searching all the source files the debugger has seen. -sources.search.key=P +sources.search.key2=CmdOrCtrl+P + +# LOCALIZATION NOTE (sources.search.alt.key): A second key shortcut to open the +# search for searching all the source files the debugger has seen. +sources.search.alt.key=CmdOrCtrl+O # LOCALIZATION NOTE (sources.noSourcesAvailable): Text shown when the debugger # does not have any sources. sources.noSourcesAvailable=This page has no sources -# LOCALIZATION NOTE (sourceSearch.search.key): Key shortcut to open the search +# LOCALIZATION NOTE (sourceSearch.search.key2): Key shortcut to open the search # for searching within a the currently opened files in the editor -sourceSearch.search.key=F +sourceSearch.search.key2=CmdOrCtrl+F # LOCALIZATION NOTE (sourceSearch.search.placeholder): placeholder text in # the source search input bar sourceSearch.search.placeholder=Search in file… -# LOCALIZATION NOTE (sourceSearch.search.again.key): Key shortcut to re-open -# the search for re-searching the same search triggered from a sourceSearch -sourceSearch.search.again.key=G +# LOCALIZATION NOTE (sourceSearch.search.again.key2): Key shortcut to highlight +# the next occurrence of the last search triggered from a source search +sourceSearch.search.again.key2=CmdOrCtrl+G + +# LOCALIZATION NOTE (sourceSearch.search.againPrev.key2): Key shortcut to highlight +# the previous occurrence of the last search triggered from a source search +sourceSearch.search.againPrev.key2=CmdOrCtrl+Shift+G # LOCALIZATION NOTE (sourceSearch.resultsSummary1): Shows a summary of # the number of matches for autocomplete @@ -552,9 +560,9 @@ symbolSearch.search.functionsPlaceholder=Search functions… # text displayed when the user searches for variables in a file symbolSearch.search.variablesPlaceholder=Search variables… -# LOCALIZATION NOTE(symbolSearch.search.key): The shortcut (cmd+shift+o) for +# LOCALIZATION NOTE(symbolSearch.search.key2): The Key Shortcut for # searching for a function or variable -symbolSearch.search.key=O +symbolSearch.search.key2=CmdOrCtrl+Shift+O # LOCALIZATION NOTE(symbolSearch.searchModifier.regex): A search option # when searching text in a file From c0c0f3b8e5e58b72399f893a3f95dd3a73e16911 Mon Sep 17 00:00:00 2001 From: Paolo Amadini Date: Fri, 5 May 2017 12:16:42 +0100 Subject: [PATCH 28/37] Bug 1364050 - Part 1 - Remove the MOZ_JSDOWNLOADS configure option. r=mak MozReview-Commit-ID: B73Ym5tD06R --HG-- extra : rebase_source : 693137f9996790a780d8ca2eaf952e8747c75246 --- browser/confvars.sh | 1 - old-configure.in | 5 - .../downloads/nsDownloadManager.cpp | 6 - uriloader/exthandler/tests/moz.build | 5 - .../tests/unit_ipc/test_encoding.js | 231 ------------------ .../exthandler/tests/unit_ipc/xpcshell.ini | 7 - 6 files changed, 255 deletions(-) delete mode 100644 uriloader/exthandler/tests/unit_ipc/test_encoding.js delete mode 100644 uriloader/exthandler/tests/unit_ipc/xpcshell.ini diff --git a/browser/confvars.sh b/browser/confvars.sh index eee56580c90b1..6fcb839614e7e 100755 --- a/browser/confvars.sh +++ b/browser/confvars.sh @@ -60,7 +60,6 @@ else MAR_CHANNEL_ID=firefox-mozilla-central fi MOZ_PROFILE_MIGRATOR=1 -MOZ_JSDOWNLOADS=1 # Enable checking that add-ons are signed by the trusted root MOZ_ADDON_SIGNING=1 diff --git a/old-configure.in b/old-configure.in index 08a0d8ded7dc4..5b3d7eed8180c 100644 --- a/old-configure.in +++ b/old-configure.in @@ -4990,11 +4990,6 @@ if test "$MOZ_REQUIRE_SIGNING" = 1; then AC_DEFINE(MOZ_REQUIRE_SIGNING) fi -AC_SUBST(MOZ_JSDOWNLOADS) -if test -n "$MOZ_JSDOWNLOADS"; then - AC_DEFINE(MOZ_JSDOWNLOADS) -fi - dnl ======================================================== dnl = Mac bundle name prefix dnl ======================================================== diff --git a/toolkit/components/downloads/nsDownloadManager.cpp b/toolkit/components/downloads/nsDownloadManager.cpp index 18805a0b4b2f2..7d90475aada71 100644 --- a/toolkit/components/downloads/nsDownloadManager.cpp +++ b/toolkit/components/downloads/nsDownloadManager.cpp @@ -958,13 +958,7 @@ nsDownloadManager::Init() getter_AddRefs(mBundle)); NS_ENSURE_SUCCESS(rv, rv); -#if !defined(MOZ_JSDOWNLOADS) - // When MOZ_JSDOWNLOADS is undefined, we still check the preference that can - // be used to enable the JavaScript API during the migration process. - mUseJSTransfer = Preferences::GetBool(PREF_BD_USEJSTRANSFER, false); -#else mUseJSTransfer = true; -#endif if (mUseJSTransfer) return NS_OK; diff --git a/uriloader/exthandler/tests/moz.build b/uriloader/exthandler/tests/moz.build index 543dae69601fd..916dc3eec1191 100644 --- a/uriloader/exthandler/tests/moz.build +++ b/uriloader/exthandler/tests/moz.build @@ -10,11 +10,6 @@ XPCSHELL_TESTS_MANIFESTS += ['unit/xpcshell.ini'] BROWSER_CHROME_MANIFESTS += ['mochitest/browser.ini'] -# The encoding test is already implemented in the Downloads API by a set of -# test cases with the string "content_encoding" in their names. -if not CONFIG['MOZ_JSDOWNLOADS']: - XPCSHELL_TESTS_MANIFESTS += ['unit_ipc/xpcshell.ini'] - TESTING_JS_MODULES += [ 'HandlerServiceTestUtils.jsm', ] diff --git a/uriloader/exthandler/tests/unit_ipc/test_encoding.js b/uriloader/exthandler/tests/unit_ipc/test_encoding.js deleted file mode 100644 index a2a00937a1c4b..0000000000000 --- a/uriloader/exthandler/tests/unit_ipc/test_encoding.js +++ /dev/null @@ -1,231 +0,0 @@ - -var Cc = Components.classes; -var Ci = Components.interfaces; -var Cu = Components.utils; -var Cr = Components.results; - -Cu.import("resource://testing-common/httpd.js"); -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -Cu.import("resource://gre/modules/NetUtil.jsm"); -Cu.import("resource://testing-common/MockRegistrar.js"); - -do_get_profile(); - -var DownloadListener = { - init: function () { - let obs = Services.obs; - obs.addObserver(this, "dl-done", true); - }, - - observe: function (subject, topic, data) { - this.onFinished(subject, topic, data); - }, - - QueryInterface: function (iid) { - if (iid.equals(Ci.nsIObserver) || - iid.equals(Ci.nsISupportsWeakReference) || - iid.equals(Ci.nsISupports)) - return this; - - throw Cr.NS_ERROR_NO_INTERFACE; - } -} -DownloadListener.init(); - -function HelperAppDlg() { } -HelperAppDlg.prototype = { - QueryInterface: XPCOMUtils.generateQI([Ci.nsIHelperAppLauncherDialog]), - show: function (launcher, ctx, reason, usePrivateUI) { - launcher.MIMEInfo.preferredAction = Ci.nsIMIMEInfo.saveToFile; - launcher.launchWithApplication(null, false); - } -} - -// Override the download-manager-ui to prevent anyone from trying to open -// a window. -function DownloadMgrUI() { } -DownloadMgrUI.prototype = { - QueryInterface: XPCOMUtils.generateQI([Ci.nsIDownloadManagerUI]), - show: function (ir, aID, reason) { }, - - visible: false, - - getAttention: function () { } -} - -function AlertsSVC() { } -AlertsSVC.prototype = { - QueryInterface: XPCOMUtils.generateQI([Ci.nsIAlertsService]), - showAlertNotification: function (url, title, text, clickable, cookie, listener, name) { }, -} - -MockRegistrar.register("@mozilla.org/helperapplauncherdialog;1", - HelperAppDlg); -MockRegistrar.register("@mozilla.org/download-manager-ui;1", - DownloadMgrUI); -MockRegistrar.register("@mozilla.org/alerts-service;1", - AlertsSVC); - -function initChildTestEnv() -{ - sendCommand(' \ - const Cc = Components.classes; \ - const Ci = Components.interfaces; \ - const Cr = Components.results; \ - const Cu = Components.utils; \ - Cu.import("resource://gre/modules/Services.jsm"); \ - function WindowContext() { } \ - \ - WindowContext.prototype = { \ - getInterface: function (iid) { \ - if (iid.equals(Ci.nsIInterfaceRequestor) || \ - iid.equals(Ci.nsIURIContentListener) || \ - iid.equals(Ci.nsILoadGroup) || \ - iid.equals(Ci.nsIDocumentLoader) || \ - iid.equals(Ci.nsIDOMWindow)) \ - return this; \ - \ - throw Cr.NS_ERROR_NO_INTERFACE; \ - }, \ - \ - /* nsIURIContentListener */ \ - onStartURIOpen: function (uri) { }, \ - isPreferred: function (type, desiredtype) { return false; }, \ - \ - /* nsILoadGroup */ \ - addRequest: function (request, context) { }, \ - removeRequest: function (request, context, status) { } \ - }; \ - \ - var ioservice = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);\ - var uriloader = Cc["@mozilla.org/uriloader;1"].getService(Ci.nsIURILoader);\ - '); -} - -function testFinisher(endFunc) { - let ef = endFunc; - return function (file) { - ef(file); - runNextTest(); - } -} - -function runChildTestSet(set) -{ - DownloadListener.onFinished = testFinisher(set[2]); - sendCommand('\ - let uri = ioservice.newURI("http://localhost:4444' + set[0] + '", null, null); \ - let channel = NetUtil.newChannel({uri: uri, loadUsingSystemPrincipal: true}); \ - uriloader.openURI(channel, Ci.nsIURILoader.IS_CONTENT_PREFERRED, new WindowContext()); \ - '); -} - -var httpserver = null; -var currentTest = 0; -function runNextTest() -{ - if (currentTest == tests.length) { - httpserver.stop(do_test_finished); - return; - } - - let set = tests[currentTest++]; - runChildTestSet(set); -} - -const responseBody = [0x1f, 0x8b, 0x08, 0x00, 0x16, 0x5a, 0x8a, 0x48, 0x02, - 0x03, 0x2b, 0x49, 0x2d, 0x2e, 0xe1, 0x02, 0x00, 0xc6, - 0x35, 0xb9, 0x3b, 0x05, 0x00, 0x00, 0x00]; - -/* - * First test: a file with Content-Type application/x-gzip and Content-Encoding gzip - * should not be decoded in a round-trip - */ -function testResponse1(metadata, response) { - response.setHeader("Content-Type", "application/x-gzip", false); - response.setHeader("Content-Encoding", "gzip", false); - response.setHeader("Content-Disposition", "attachment", false); - - var bos = Cc["@mozilla.org/binaryoutputstream;1"].createInstance(Ci.nsIBinaryOutputStream); - bos.setOutputStream(response.bodyOutputStream); - bos.writeByteArray(responseBody, responseBody.length); -} - -function finishTest1(subject, topic, data) { - let file = subject.QueryInterface(Ci.nsIDownload).targetFile; - do_check_true(file.path.search("test1.gz") != 0); - let fis = Cc["@mozilla.org/network/file-input-stream;1"].createInstance(Ci.nsIFileInputStream); - fis.init(file, -1, -1, 0); - let bis = Cc["@mozilla.org/binaryinputstream;1"].createInstance(Ci.nsIBinaryInputStream); - bis.setInputStream(fis); - let str = bis.readByteArray(bis.available()); - do_check_matches(str, responseBody); -} - -/* - * Second test: a file with Content-Type text/html and Content-Encoding gzip - * should not be decoded in a round-trip, if its filename ends in ".gz". - * We specify a Content-disposition header to force it to be saved as a file. - */ -function testResponse2(metadata, response) { - response.setHeader("Content-Type", "text/html", false); - response.setHeader("Content-Encoding", "gzip", false); - response.setHeader("Content-Disposition", "attachment", false); - - var bos = Cc["@mozilla.org/binaryoutputstream;1"].createInstance(Ci.nsIBinaryOutputStream); - bos.setOutputStream(response.bodyOutputStream); - bos.writeByteArray(responseBody, responseBody.length); -} - -function finishTest2(subject, topic, data) { - let file = subject.QueryInterface(Ci.nsIDownload).targetFile; - do_check_true(file.path.search("test2.gz") != 0); - let fis = Cc["@mozilla.org/network/file-input-stream;1"].createInstance(Ci.nsIFileInputStream); - fis.init(file, -1, -1, 0); - let bis = Cc["@mozilla.org/binaryinputstream;1"].createInstance(Ci.nsIBinaryInputStream); - bis.setInputStream(fis); - let str = bis.readByteArray(bis.available()); - do_check_matches(str, responseBody); -} - -function testResponse3(metadata, response) { - response.setHeader("Content-Type", "text/html", false); - response.setHeader("Content-Encoding", "gzip", false); - response.setHeader("Content-Disposition", "attachment", false); - - var bos = Cc["@mozilla.org/binaryoutputstream;1"].createInstance(Ci.nsIBinaryOutputStream); - bos.setOutputStream(response.bodyOutputStream); - bos.writeByteArray(responseBody, responseBody.length); -} - -function finishTest3(subject, topic, data) { - let file = subject.QueryInterface(Ci.nsIDownload).targetFile; - do_check_true(file.path.search("test3.txt") != 0); - let fis = Cc["@mozilla.org/network/file-input-stream;1"].createInstance(Ci.nsIFileInputStream); - fis.init(file, -1, -1, 0); - let bis = Cc["@mozilla.org/binaryinputstream;1"].createInstance(Ci.nsIBinaryInputStream); - bis.setInputStream(fis); - let str = bis.readByteArray(bis.available()); - let decodedBody = [ 116, 101, 115, 116, 10 ]; // 't','e','s','t','\n' - do_check_matches(str, decodedBody); -} - -var tests = [ - [ "/test1.gz", testResponse1, finishTest1 ], - [ "/test2.gz", testResponse2, finishTest2 ], - [ "/test3.txt", testResponse3, finishTest3 ], -]; - -function run_test() { -// do_load_child_test_harness(); - httpserver = new HttpServer(); - httpserver.start(4444); - do_test_pending(); - - initChildTestEnv(); - - for (let set of tests) - httpserver.registerPathHandler(set[0], set[1]); - - runNextTest(); -} diff --git a/uriloader/exthandler/tests/unit_ipc/xpcshell.ini b/uriloader/exthandler/tests/unit_ipc/xpcshell.ini deleted file mode 100644 index f7edd8045e748..0000000000000 --- a/uriloader/exthandler/tests/unit_ipc/xpcshell.ini +++ /dev/null @@ -1,7 +0,0 @@ -[DEFAULT] -head = - -[test_encoding.js] -# Bug 676995: test hangs consistently on Android -# Bug 907732: thunderbird still uses legacy downloads manager. -skip-if = (os == "android" || buildapp == '../mail') From f85ca5d42475df02d471ede189d2c9d4d7aceee7 Mon Sep 17 00:00:00 2001 From: Paolo Amadini Date: Fri, 5 May 2017 13:06:59 +0100 Subject: [PATCH 29/37] Bug 1364050 - Part 2 - Remove unused strings. r=mak MozReview-Commit-ID: HW4Zkxbe1od --HG-- extra : rebase_source : a1c0b8c1fd6d08c6a8423459ea6d96992f30e2e6 --- .../chrome/mozapps/downloads/downloads.properties | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/toolkit/locales/en-US/chrome/mozapps/downloads/downloads.properties b/toolkit/locales/en-US/chrome/mozapps/downloads/downloads.properties index 8797b338daf60..856c49b8abc9f 100644 --- a/toolkit/locales/en-US/chrome/mozapps/downloads/downloads.properties +++ b/toolkit/locales/en-US/chrome/mozapps/downloads/downloads.properties @@ -22,14 +22,6 @@ shortHours=h;h # d is the short form for days shortDays=d;d -# LOCALIZATION NOTE (paused): — is the "em dash" (long dash) -paused=Paused — #1 -downloading=Downloading -notStarted=Not Started -failed=Failed -finished=Finished -canceled=Canceled - downloadErrorAlertTitle=Download Error downloadErrorGeneric=The download cannot be saved because an unknown error occurred.\n\nPlease try again. @@ -51,8 +43,6 @@ dontQuitButtonWin=Don’t Exit dontQuitButtonMac=Don’t Quit dontGoOfflineButton=Stay Online dontLeavePrivateBrowsingButton2=Stay in Private Browsing -downloadsCompleteTitle=Downloads Complete -downloadsCompleteMsg=All files have finished downloading. # LOCALIZATION NOTE (infiniteRate): # If download speed is a JavaScript Infinity value, this phrase is used From 93316227a724ced3270d6300d16c133f54741fd2 Mon Sep 17 00:00:00 2001 From: Paolo Amadini Date: Mon, 15 May 2017 11:55:50 +0100 Subject: [PATCH 30/37] Bug 1364050 - Part 3 - Remove obsolete code from the nsIDownloadManager implementation. r=mak A missing dependency in "nsTypeAheadFind.h" is also fixed to allow the removal of the include files from "nsToolkitCompsModule.cpp". MozReview-Commit-ID: EefZA9bEUW0 --HG-- extra : rebase_source : 8ddfaf72f2b89c6e906561710045ec677e46da79 --- .../static/browser_all_files_referenced.js | 4 +- .../components/build/nsToolkitCompsModule.cpp | 4 - toolkit/components/downloads/SQLFunctions.cpp | 146 - toolkit/components/downloads/SQLFunctions.h | 46 - toolkit/components/downloads/generate_csd.sh | 2 +- toolkit/components/downloads/moz.build | 12 - .../downloads/nsDownloadManager.cpp | 3828 +---------------- .../components/downloads/nsDownloadManager.h | 417 +- .../components/downloads/nsDownloadProxy.h | 179 - .../downloads/nsDownloadScanner.cpp | 728 ---- .../components/downloads/nsDownloadScanner.h | 121 - .../typeaheadfind/nsTypeAheadFind.h | 1 + 12 files changed, 234 insertions(+), 5254 deletions(-) delete mode 100644 toolkit/components/downloads/SQLFunctions.cpp delete mode 100644 toolkit/components/downloads/SQLFunctions.h delete mode 100644 toolkit/components/downloads/nsDownloadProxy.h delete mode 100644 toolkit/components/downloads/nsDownloadScanner.cpp delete mode 100644 toolkit/components/downloads/nsDownloadScanner.h diff --git a/browser/base/content/test/static/browser_all_files_referenced.js b/browser/base/content/test/static/browser_all_files_referenced.js index cf8f6000d396f..89cb9d68485be 100644 --- a/browser/base/content/test/static/browser_all_files_referenced.js +++ b/browser/base/content/test/static/browser_all_files_referenced.js @@ -61,6 +61,9 @@ var whitelist = new Set([ {file: "chrome://devtools/content/inspector/markup/markup.xhtml", isFromDevTools: true}, + // Kept for add-on compatibility, should be removed in bug 851471. + {file: "chrome://mozapps/skin/downloads/downloadIcon.png"}, + // extensions/pref/autoconfig/src/nsReadConfig.cpp {file: "resource://gre/defaults/autoconfig/prefcalls.js"}, @@ -111,7 +114,6 @@ var whitelist = new Set([ // browser/extensions/pdfjs/content/web/viewer.js#7450 {file: "resource://pdf.js/web/debugger.js"}, - // Starting from here, files in the whitelist are bugs that need fixing. // Bug 1339420 {file: "chrome://branding/content/icon128.png"}, diff --git a/toolkit/components/build/nsToolkitCompsModule.cpp b/toolkit/components/build/nsToolkitCompsModule.cpp index dd6eac9ef540e..53cd98b9a19fe 100644 --- a/toolkit/components/build/nsToolkitCompsModule.cpp +++ b/toolkit/components/build/nsToolkitCompsModule.cpp @@ -21,7 +21,6 @@ #include "nsDownloadManager.h" #include "DownloadPlatform.h" -#include "nsDownloadProxy.h" #include "rdf.h" #include "nsTypeAheadFind.h" @@ -88,7 +87,6 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(nsAlertsService) NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsDownloadManager, nsDownloadManager::GetSingleton) NS_GENERIC_FACTORY_CONSTRUCTOR(DownloadPlatform) -NS_GENERIC_FACTORY_CONSTRUCTOR(nsDownloadProxy) NS_GENERIC_FACTORY_CONSTRUCTOR(nsTypeAheadFind) @@ -147,7 +145,6 @@ NS_DEFINE_NAMED_CID(NS_PARENTALCONTROLSSERVICE_CID); #endif NS_DEFINE_NAMED_CID(NS_DOWNLOADMANAGER_CID); NS_DEFINE_NAMED_CID(NS_DOWNLOADPLATFORM_CID); -NS_DEFINE_NAMED_CID(NS_DOWNLOAD_CID); NS_DEFINE_NAMED_CID(NS_FIND_SERVICE_CID); NS_DEFINE_NAMED_CID(NS_TYPEAHEADFIND_CID); NS_DEFINE_NAMED_CID(NS_APPLICATION_REPUTATION_SERVICE_CID); @@ -183,7 +180,6 @@ static const Module::CIDEntry kToolkitCIDs[] = { #endif { &kNS_DOWNLOADMANAGER_CID, false, nullptr, nsDownloadManagerConstructor }, { &kNS_DOWNLOADPLATFORM_CID, false, nullptr, DownloadPlatformConstructor }, - { &kNS_DOWNLOAD_CID, false, nullptr, nsDownloadProxyConstructor }, { &kNS_FIND_SERVICE_CID, false, nullptr, nsFindServiceConstructor }, { &kNS_TYPEAHEADFIND_CID, false, nullptr, nsTypeAheadFindConstructor }, { &kNS_APPLICATION_REPUTATION_SERVICE_CID, false, nullptr, ApplicationReputationServiceConstructor }, diff --git a/toolkit/components/downloads/SQLFunctions.cpp b/toolkit/components/downloads/SQLFunctions.cpp deleted file mode 100644 index 8f2d3e77bc0bb..0000000000000 --- a/toolkit/components/downloads/SQLFunctions.cpp +++ /dev/null @@ -1,146 +0,0 @@ -/* vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ : - * 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 "mozilla/storage.h" -#include "mozilla/storage/Variant.h" -#include "mozilla/mozalloc.h" -#include "nsString.h" -#include "SQLFunctions.h" -#include "nsUTF8Utils.h" -#include "plbase64.h" -#include "prio.h" - -#ifdef XP_WIN -#include -#include -#endif - -// The length of guids that are used by the download manager -#define GUID_LENGTH 12 - -namespace mozilla { -namespace downloads { - -// Keep this file in sync with the GUID-related code in toolkit/places/SQLFunctions.cpp -// and toolkit/places/Helpers.cpp! - -//////////////////////////////////////////////////////////////////////////////// -//// GUID Creation Function - -////////////////////////////////////////////////////////////////////////////// -//// GenerateGUIDFunction - -/* static */ -nsresult -GenerateGUIDFunction::create(mozIStorageConnection *aDBConn) -{ - RefPtr function = new GenerateGUIDFunction(); - nsresult rv = aDBConn->CreateFunction( - NS_LITERAL_CSTRING("generate_guid"), 0, function - ); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; -} - -NS_IMPL_ISUPPORTS( - GenerateGUIDFunction, - mozIStorageFunction -) - -static -nsresult -Base64urlEncode(const uint8_t* aBytes, - uint32_t aNumBytes, - nsCString& _result) -{ - // SetLength does not set aside space for null termination. PL_Base64Encode - // will not null terminate, however, nsCStrings must be null terminated. As a - // result, we set the capacity to be one greater than what we need, and the - // length to our desired length. - uint32_t length = (aNumBytes + 2) / 3 * 4; // +2 due to integer math. - NS_ENSURE_TRUE(_result.SetCapacity(length + 1, mozilla::fallible), - NS_ERROR_OUT_OF_MEMORY); - _result.SetLength(length); - (void)PL_Base64Encode(reinterpret_cast(aBytes), aNumBytes, - _result.BeginWriting()); - - // base64url encoding is defined in RFC 4648. It replaces the last two - // alphabet characters of base64 encoding with '-' and '_' respectively. - _result.ReplaceChar('+', '-'); - _result.ReplaceChar('/', '_'); - return NS_OK; -} - -static -nsresult -GenerateRandomBytes(uint32_t aSize, - uint8_t* _buffer) -{ - // On Windows, we'll use its built-in cryptographic API. -#if defined(XP_WIN) - HCRYPTPROV cryptoProvider; - BOOL rc = CryptAcquireContext(&cryptoProvider, 0, 0, PROV_RSA_FULL, - CRYPT_VERIFYCONTEXT | CRYPT_SILENT); - if (rc) { - rc = CryptGenRandom(cryptoProvider, aSize, _buffer); - (void)CryptReleaseContext(cryptoProvider, 0); - } - return rc ? NS_OK : NS_ERROR_FAILURE; - - // On Unix, we'll just read in from /dev/urandom. -#elif defined(XP_UNIX) - NS_ENSURE_ARG_MAX(aSize, INT32_MAX); - PRFileDesc* urandom = PR_Open("/dev/urandom", PR_RDONLY, 0); - nsresult rv = NS_ERROR_FAILURE; - if (urandom) { - int32_t bytesRead = PR_Read(urandom, _buffer, aSize); - if (bytesRead == static_cast(aSize)) { - rv = NS_OK; - } - (void)PR_Close(urandom); - } - return rv; -#endif -} - -nsresult -GenerateGUID(nsCString& _guid) -{ - _guid.Truncate(); - - // Request raw random bytes and base64url encode them. For each set of three - // bytes, we get one character. - const uint32_t kRequiredBytesLength = - static_cast(GUID_LENGTH / 4 * 3); - - uint8_t buffer[kRequiredBytesLength]; - nsresult rv = GenerateRandomBytes(kRequiredBytesLength, buffer); - NS_ENSURE_SUCCESS(rv, rv); - - rv = Base64urlEncode(buffer, kRequiredBytesLength, _guid); - NS_ENSURE_SUCCESS(rv, rv); - - NS_ASSERTION(_guid.Length() == GUID_LENGTH, "GUID is not the right size!"); - return NS_OK; -} - -////////////////////////////////////////////////////////////////////////////// -//// mozIStorageFunction - -NS_IMETHODIMP -GenerateGUIDFunction::OnFunctionCall(mozIStorageValueArray *aArguments, - nsIVariant **_result) -{ - nsAutoCString guid; - nsresult rv = GenerateGUID(guid); - NS_ENSURE_SUCCESS(rv, rv); - - NS_ADDREF(*_result = new mozilla::storage::UTF8TextVariant(guid)); - return NS_OK; -} - -} // namespace downloads -} // namespace mozilla diff --git a/toolkit/components/downloads/SQLFunctions.h b/toolkit/components/downloads/SQLFunctions.h deleted file mode 100644 index ae207788c21f4..0000000000000 --- a/toolkit/components/downloads/SQLFunctions.h +++ /dev/null @@ -1,46 +0,0 @@ -/* vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ : - * 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/. */ - -#ifndef mozilla_downloads_SQLFunctions_h -#define mozilla_downloads_SQLFunctions_h - -#include "mozIStorageFunction.h" -#include "mozilla/Attributes.h" - -class nsCString; -class mozIStorageConnection; - -namespace mozilla { -namespace downloads { - -/** - * SQL function to generate a GUID for a place or bookmark item. This is just - * a wrapper around GenerateGUID in SQLFunctions.cpp. - * - * @return a guid for the item. - * @see toolkit/components/places/SQLFunctions.h - keep this in sync - */ -class GenerateGUIDFunction final : public mozIStorageFunction -{ - ~GenerateGUIDFunction() {} -public: - NS_DECL_THREADSAFE_ISUPPORTS - NS_DECL_MOZISTORAGEFUNCTION - - /** - * Registers the function with the specified database connection. - * - * @param aDBConn - * The database connection to register with. - */ - static nsresult create(mozIStorageConnection *aDBConn); -}; - -nsresult GenerateGUID(nsCString& _guid); - -} // namespace downloads -} // namespace mozilla - -#endif diff --git a/toolkit/components/downloads/generate_csd.sh b/toolkit/components/downloads/generate_csd.sh index a322d4c3c4292..908e88d6bbb8c 100755 --- a/toolkit/components/downloads/generate_csd.sh +++ b/toolkit/components/downloads/generate_csd.sh @@ -19,7 +19,7 @@ if [ ! -e $PROTOC_PATH ]; then exit 1 fi -if [ ! -f nsDownloadManager.cpp ]; then +if [ ! -f nsIApplicationReputation.idl ]; then echo "You must run this script in the toolkit/components/downloads" >&2 echo "directory of the source tree." >&2 exit 1 diff --git a/toolkit/components/downloads/moz.build b/toolkit/components/downloads/moz.build index 91f69297c68a6..a56b15ea4e12d 100644 --- a/toolkit/components/downloads/moz.build +++ b/toolkit/components/downloads/moz.build @@ -37,18 +37,6 @@ UNIFIED_SOURCES += [ 'nsDownloadManager.cpp' ] -# SQLFunctions.cpp cannot be built in unified mode because of Windows headers. -SOURCES += [ - 'SQLFunctions.cpp', -] - -if CONFIG['OS_ARCH'] == 'WINNT': - # Can't build unified because we need CreateEvent which some IPC code - # included in LoadContext ends up undefining. - SOURCES += [ - 'nsDownloadScanner.cpp', - ] - FINAL_LIBRARY = 'xul' LOCAL_INCLUDES += [ diff --git a/toolkit/components/downloads/nsDownloadManager.cpp b/toolkit/components/downloads/nsDownloadManager.cpp index 7d90475aada71..cfe2add9b3beb 100644 --- a/toolkit/components/downloads/nsDownloadManager.cpp +++ b/toolkit/components/downloads/nsDownloadManager.cpp @@ -3,92 +3,15 @@ * 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 "mozilla/DebugOnly.h" -#include "mozilla/Unused.h" - -#include "mozIStorageService.h" -#include "nsIAlertsService.h" -#include "nsIArray.h" -#include "nsIClassInfoImpl.h" -#include "nsIDOMWindow.h" -#include "nsIDownloadHistory.h" -#include "nsIDownloadManagerUI.h" -#include "nsIFileURL.h" -#include "nsIMIMEService.h" -#include "nsIParentalControlsService.h" #include "nsIPrefService.h" -#include "nsIPrivateBrowsingChannel.h" -#include "nsIPromptService.h" #include "nsIPropertyBag2.h" -#include "nsIResumableChannel.h" -#include "nsIWebBrowserPersist.h" -#include "nsIWindowMediator.h" -#include "nsILocalFileWin.h" -#include "nsILoadContext.h" -#include "nsIXULAppInfo.h" -#include "nsContentUtils.h" - -#include "nsAppDirectoryServiceDefs.h" -#include "nsArrayEnumerator.h" #include "nsCExternalHandlerService.h" -#include "nsCRTGlue.h" #include "nsDirectoryServiceDefs.h" #include "nsDownloadManager.h" -#include "nsNetUtil.h" -#include "nsThreadUtils.h" -#include "prtime.h" - -#include "mozStorageCID.h" -#include "nsDocShellCID.h" -#include "nsEmbedCID.h" -#include "nsToolkitCompsCID.h" - -#include "mozilla/net/ReferrerPolicy.h" - -#include "SQLFunctions.h" - -#include "mozilla/Preferences.h" - -#ifdef XP_WIN -#include -#include "nsWindowsHelpers.h" -#ifdef DOWNLOAD_SCANNER -#include "nsDownloadScanner.h" -#endif -#endif - -#ifdef XP_MACOSX -#include -#endif - -#ifdef MOZ_WIDGET_ANDROID -#include "FennecJNIWrappers.h" -#endif - -#ifdef MOZ_WIDGET_GTK -#include -#endif using namespace mozilla; -using mozilla::downloads::GenerateGUID; #define DOWNLOAD_MANAGER_BUNDLE "chrome://mozapps/locale/downloads/downloads.properties" -#define DOWNLOAD_MANAGER_ALERT_ICON "chrome://mozapps/skin/downloads/downloadIcon.png" -#define PREF_BD_USEJSTRANSFER "browser.download.useJSTransfer" -#define PREF_BDM_SHOWALERTONCOMPLETE "browser.download.manager.showAlertOnComplete" -#define PREF_BDM_SHOWALERTINTERVAL "browser.download.manager.showAlertInterval" -#define PREF_BDM_RETENTION "browser.download.manager.retention" -#define PREF_BDM_QUITBEHAVIOR "browser.download.manager.quitBehavior" -#define PREF_BDM_ADDTORECENTDOCS "browser.download.manager.addToRecentDocs" -#define PREF_BDM_SCANWHENDONE "browser.download.manager.scanWhenDone" -#define PREF_BDM_RESUMEONWAKEDELAY "browser.download.manager.resumeOnWakeDelay" -#define PREF_BH_DELETETEMPFILEONEXIT "browser.helperApps.deleteTempFileOnExit" - -static const int64_t gUpdateInterval = 400 * PR_USEC_PER_MSEC; - -#define DM_SCHEMA_VERSION 9 -#define DM_DB_NAME NS_LITERAL_STRING("downloads.sqlite") -#define DM_DB_CORRUPT_FILENAME NS_LITERAL_STRING("downloads.sqlite.corrupt") #define NS_SYSTEMINFO_CONTRACTID "@mozilla.org/system-info;1" @@ -98,9 +21,6 @@ static const int64_t gUpdateInterval = 400 * PR_USEC_PER_MSEC; NS_IMPL_ISUPPORTS( nsDownloadManager , nsIDownloadManager -, nsINavHistoryObserver -, nsIObserver -, nsISupportsWeakReference ) nsDownloadManager *nsDownloadManager::gDownloadManagerService = nullptr; @@ -115,9 +35,6 @@ nsDownloadManager::GetSingleton() gDownloadManagerService = new nsDownloadManager(); if (gDownloadManagerService) { -#if defined(MOZ_WIDGET_GTK) - g_type_init(); -#endif NS_ADDREF(gDownloadManagerService); if (NS_FAILED(gDownloadManagerService->Init())) NS_RELEASE(gDownloadManagerService); @@ -128,3651 +45,362 @@ nsDownloadManager::GetSingleton() nsDownloadManager::~nsDownloadManager() { -#ifdef DOWNLOAD_SCANNER - if (mScanner) { - delete mScanner; - mScanner = nullptr; - } -#endif gDownloadManagerService = nullptr; } nsresult -nsDownloadManager::ResumeRetry(nsDownload *aDl) +nsDownloadManager::Init() { - // Keep a reference in case we need to cancel the download - RefPtr dl = aDl; - - // Try to resume the active download - nsresult rv = dl->Resume(); - - // If not, try to retry the download - if (NS_FAILED(rv)) { - // First cancel the download so it's no longer active - rv = dl->Cancel(); - - // Then retry it - if (NS_SUCCEEDED(rv)) - rv = dl->Retry(); - } + nsresult rv; - return rv; -} + nsCOMPtr bundleService = + mozilla::services::GetStringBundleService(); + if (!bundleService) + return NS_ERROR_FAILURE; -nsresult -nsDownloadManager::PauseAllDownloads(bool aSetResume) -{ - nsresult rv = PauseAllDownloads(mCurrentDownloads, aSetResume); - nsresult rv2 = PauseAllDownloads(mCurrentPrivateDownloads, aSetResume); + rv = bundleService->CreateBundle(DOWNLOAD_MANAGER_BUNDLE, + getter_AddRefs(mBundle)); NS_ENSURE_SUCCESS(rv, rv); - NS_ENSURE_SUCCESS(rv2, rv2); + return NS_OK; } -nsresult -nsDownloadManager::PauseAllDownloads(nsCOMArray& aDownloads, bool aSetResume) -{ - nsresult retVal = NS_OK; - for (int32_t i = aDownloads.Count() - 1; i >= 0; --i) { - RefPtr dl = aDownloads[i]; - - // Only pause things that need to be paused - if (!dl->IsPaused()) { - // Set auto-resume before pausing so that it gets into the DB - dl->mAutoResume = aSetResume ? nsDownload::AUTO_RESUME : - nsDownload::DONT_RESUME; - - // Try to pause the download but don't bail now if we fail - nsresult rv = dl->Pause(); - if (NS_FAILED(rv)) - retVal = rv; - } - } +//////////////////////////////////////////////////////////////////////////////// +//// nsIDownloadManager - return retVal; +NS_IMETHODIMP +nsDownloadManager::GetActivePrivateDownloadCount(int32_t* aResult) +{ + return NS_ERROR_UNEXPECTED; } -nsresult -nsDownloadManager::ResumeAllDownloads(bool aResumeAll) +NS_IMETHODIMP +nsDownloadManager::GetActiveDownloadCount(int32_t *aResult) { - nsresult rv = ResumeAllDownloads(mCurrentDownloads, aResumeAll); - nsresult rv2 = ResumeAllDownloads(mCurrentPrivateDownloads, aResumeAll); - NS_ENSURE_SUCCESS(rv, rv); - NS_ENSURE_SUCCESS(rv2, rv2); - return NS_OK; + return NS_ERROR_UNEXPECTED; } -nsresult -nsDownloadManager::ResumeAllDownloads(nsCOMArray& aDownloads, bool aResumeAll) +NS_IMETHODIMP +nsDownloadManager::GetActiveDownloads(nsISimpleEnumerator **aResult) { - nsresult retVal = NS_OK; - for (int32_t i = aDownloads.Count() - 1; i >= 0; --i) { - RefPtr dl = aDownloads[i]; - - // If aResumeAll is true, then resume everything; otherwise, check if the - // download should auto-resume - if (aResumeAll || dl->ShouldAutoResume()) { - // Reset auto-resume before retrying so that it gets into the DB through - // ResumeRetry's eventual call to SetState. We clear the value now so we - // don't accidentally query completed downloads that were previously - // auto-resumed (and try to resume them). - dl->mAutoResume = nsDownload::DONT_RESUME; - - // Try to resume/retry the download but don't bail now if we fail - nsresult rv = ResumeRetry(dl); - if (NS_FAILED(rv)) - retVal = rv; - } - } - - return retVal; + return NS_ERROR_UNEXPECTED; } -nsresult -nsDownloadManager::RemoveAllDownloads() +NS_IMETHODIMP +nsDownloadManager::GetActivePrivateDownloads(nsISimpleEnumerator **aResult) { - nsresult rv = RemoveAllDownloads(mCurrentDownloads); - nsresult rv2 = RemoveAllDownloads(mCurrentPrivateDownloads); - NS_ENSURE_SUCCESS(rv, rv); - NS_ENSURE_SUCCESS(rv2, rv2); - return NS_OK; + return NS_ERROR_UNEXPECTED; } -nsresult -nsDownloadManager::RemoveAllDownloads(nsCOMArray& aDownloads) +/** + * For platforms where helper apps use the downloads directory (i.e. mobile), + * this should be kept in sync with nsExternalHelperAppService.cpp + */ +NS_IMETHODIMP +nsDownloadManager::GetDefaultDownloadsDirectory(nsIFile **aResult) { - nsresult rv = NS_OK; - for (int32_t i = aDownloads.Count() - 1; i >= 0; --i) { - RefPtr dl = aDownloads[0]; + nsCOMPtr downloadDir; - nsresult result = NS_OK; - if (!dl->mPrivate && dl->IsPaused() && GetQuitBehavior() != QUIT_AND_CANCEL) - aDownloads.RemoveObject(dl); - else - result = dl->Cancel(); + nsresult rv; + nsCOMPtr dirService = + do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv, rv); - // Track the failure, but don't miss out on other downloads - if (NS_FAILED(result)) - rv = result; - } + // OSX 10.4: + // Desktop + // OSX 10.5: + // User download directory + // Vista: + // Downloads + // XP/2K: + // My Documents/Downloads + // Linux: + // XDG user dir spec, with a fallback to Home/Downloads - return rv; -} + nsXPIDLString folderName; + mBundle->GetStringFromName(u"downloadsFolder", + getter_Copies(folderName)); -nsresult -nsDownloadManager::RemoveDownloadsForURI(mozIStorageStatement* aStatement, nsIURI *aURI) -{ - mozStorageStatementScoper scope(aStatement); +#if defined (XP_MACOSX) + rv = dirService->Get(NS_OSX_DEFAULT_DOWNLOAD_DIR, + NS_GET_IID(nsIFile), + getter_AddRefs(downloadDir)); + NS_ENSURE_SUCCESS(rv, rv); +#elif defined(XP_WIN) + rv = dirService->Get(NS_WIN_DEFAULT_DOWNLOAD_DIR, + NS_GET_IID(nsIFile), + getter_AddRefs(downloadDir)); + NS_ENSURE_SUCCESS(rv, rv); - nsAutoCString source; - nsresult rv = aURI->GetSpec(source); + // Check the os version + nsCOMPtr infoService = + do_GetService(NS_SYSTEMINFO_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); - rv = aStatement->BindUTF8StringByName( - NS_LITERAL_CSTRING("source"), source); + int32_t version; + NS_NAMED_LITERAL_STRING(osVersion, "version"); + rv = infoService->GetPropertyAsInt32(osVersion, &version); NS_ENSURE_SUCCESS(rv, rv); + if (version < 6) { // XP/2K + // First get "My Documents" + rv = dirService->Get(NS_WIN_PERSONAL_DIR, + NS_GET_IID(nsIFile), + getter_AddRefs(downloadDir)); + NS_ENSURE_SUCCESS(rv, rv); - bool hasMore = false; - AutoTArray downloads; - // Get all the downloads that match the provided URI - while (NS_SUCCEEDED(aStatement->ExecuteStep(&hasMore)) && - hasMore) { - nsAutoCString downloadGuid; - rv = aStatement->GetUTF8String(0, downloadGuid); + rv = downloadDir->Append(folderName); NS_ENSURE_SUCCESS(rv, rv); - downloads.AppendElement(downloadGuid); + // This could be the first time we are creating the downloads folder in My + // Documents, so make sure it exists. + bool exists; + rv = downloadDir->Exists(&exists); + NS_ENSURE_SUCCESS(rv, rv); + if (!exists) { + rv = downloadDir->Create(nsIFile::DIRECTORY_TYPE, 0755); + NS_ENSURE_SUCCESS(rv, rv); + } } - - // Remove each download ignoring any failure so we reach other downloads - for (int32_t i = downloads.Length(); --i >= 0; ) - (void)RemoveDownload(downloads[i]); - - return NS_OK; -} - -void // static -nsDownloadManager::ResumeOnWakeCallback(nsITimer *aTimer, void *aClosure) -{ - // Resume the downloads that were set to autoResume - nsDownloadManager *dlMgr = static_cast(aClosure); - (void)dlMgr->ResumeAllDownloads(false); -} - -already_AddRefed -nsDownloadManager::GetFileDBConnection(nsIFile *dbFile) const -{ - NS_ASSERTION(dbFile, "GetFileDBConnection called with an invalid nsIFile"); - - nsCOMPtr storage = - do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID); - NS_ENSURE_TRUE(storage, nullptr); - - nsCOMPtr conn; - nsresult rv = storage->OpenDatabase(dbFile, getter_AddRefs(conn)); - if (rv == NS_ERROR_FILE_CORRUPTED) { - // delete and try again, since we don't care so much about losing a user's - // download history - rv = dbFile->Remove(false); - NS_ENSURE_SUCCESS(rv, nullptr); - rv = storage->OpenDatabase(dbFile, getter_AddRefs(conn)); +#elif defined(XP_UNIX) +#if defined(MOZ_WIDGET_ANDROID) + // Android doesn't have a $HOME directory, and by default we only have + // write access to /data/data/org.mozilla.{$APP} and /sdcard + char* downloadDirPath = getenv("DOWNLOADS_DIRECTORY"); + if (downloadDirPath) { + rv = NS_NewNativeLocalFile(nsDependentCString(downloadDirPath), + true, getter_AddRefs(downloadDir)); + NS_ENSURE_SUCCESS(rv, rv); + } + else { + rv = NS_ERROR_FAILURE; + } +#else + rv = dirService->Get(NS_UNIX_DEFAULT_DOWNLOAD_DIR, + NS_GET_IID(nsIFile), + getter_AddRefs(downloadDir)); + // fallback to Home/Downloads + if (NS_FAILED(rv)) { + rv = dirService->Get(NS_UNIX_HOME_DIR, + NS_GET_IID(nsIFile), + getter_AddRefs(downloadDir)); + NS_ENSURE_SUCCESS(rv, rv); + rv = downloadDir->Append(folderName); + NS_ENSURE_SUCCESS(rv, rv); } - NS_ENSURE_SUCCESS(rv, nullptr); - - return conn.forget(); -} - -already_AddRefed -nsDownloadManager::GetPrivateDBConnection() const -{ - nsCOMPtr storage = - do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID); - NS_ENSURE_TRUE(storage, nullptr); - - nsCOMPtr conn; - nsresult rv = storage->OpenSpecialDatabase("memory", getter_AddRefs(conn)); - NS_ENSURE_SUCCESS(rv, nullptr); - - return conn.forget(); -} - -void -nsDownloadManager::CloseAllDBs() -{ - CloseDB(mDBConn, mUpdateDownloadStatement, mGetIdsForURIStatement); - CloseDB(mPrivateDBConn, mUpdatePrivateDownloadStatement, mGetPrivateIdsForURIStatement); -} - -void -nsDownloadManager::CloseDB(mozIStorageConnection* aDBConn, - mozIStorageStatement* aUpdateStmt, - mozIStorageStatement* aGetIdsStmt) -{ - DebugOnly rv = aGetIdsStmt->Finalize(); - MOZ_ASSERT(NS_SUCCEEDED(rv)); - rv = aUpdateStmt->Finalize(); - MOZ_ASSERT(NS_SUCCEEDED(rv)); - rv = aDBConn->AsyncClose(nullptr); - MOZ_ASSERT(NS_SUCCEEDED(rv)); -} - -static nsresult -InitSQLFunctions(mozIStorageConnection* aDBConn) -{ - nsresult rv = mozilla::downloads::GenerateGUIDFunction::create(aDBConn); +#endif +#else + rv = dirService->Get(NS_OS_HOME_DIR, + NS_GET_IID(nsIFile), + getter_AddRefs(downloadDir)); NS_ENSURE_SUCCESS(rv, rv); - return NS_OK; -} - -nsresult -nsDownloadManager::InitPrivateDB() -{ - bool ready = false; - if (mPrivateDBConn && NS_SUCCEEDED(mPrivateDBConn->GetConnectionReady(&ready)) && ready) - CloseDB(mPrivateDBConn, mUpdatePrivateDownloadStatement, mGetPrivateIdsForURIStatement); - mPrivateDBConn = GetPrivateDBConnection(); - if (!mPrivateDBConn) - return NS_ERROR_NOT_AVAILABLE; - - nsresult rv = InitSQLFunctions(mPrivateDBConn); + rv = downloadDir->Append(folderName); NS_ENSURE_SUCCESS(rv, rv); +#endif - rv = CreateTable(mPrivateDBConn); - NS_ENSURE_SUCCESS(rv, rv); + downloadDir.forget(aResult); - rv = InitStatements(mPrivateDBConn, getter_AddRefs(mUpdatePrivateDownloadStatement), - getter_AddRefs(mGetPrivateIdsForURIStatement)); - NS_ENSURE_SUCCESS(rv, rv); return NS_OK; } -nsresult -nsDownloadManager::InitFileDB() +#define NS_BRANCH_DOWNLOAD "browser.download." +#define NS_PREF_FOLDERLIST "folderList" +#define NS_PREF_DIR "dir" + +NS_IMETHODIMP +nsDownloadManager::GetUserDownloadsDirectory(nsIFile **aResult) { nsresult rv; - - nsCOMPtr dbFile; - rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, - getter_AddRefs(dbFile)); - NS_ENSURE_SUCCESS(rv, rv); - rv = dbFile->Append(DM_DB_NAME); + nsCOMPtr dirService = + do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); - bool ready = false; - if (mDBConn && NS_SUCCEEDED(mDBConn->GetConnectionReady(&ready)) && ready) - CloseDB(mDBConn, mUpdateDownloadStatement, mGetIdsForURIStatement); - mDBConn = GetFileDBConnection(dbFile); - NS_ENSURE_TRUE(mDBConn, NS_ERROR_NOT_AVAILABLE); - - rv = InitSQLFunctions(mDBConn); + nsCOMPtr prefService = + do_GetService(NS_PREFSERVICE_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); - bool tableExists; - rv = mDBConn->TableExists(NS_LITERAL_CSTRING("moz_downloads"), &tableExists); + nsCOMPtr prefBranch; + rv = prefService->GetBranch(NS_BRANCH_DOWNLOAD, + getter_AddRefs(prefBranch)); NS_ENSURE_SUCCESS(rv, rv); - if (!tableExists) { - rv = CreateTable(mDBConn); - NS_ENSURE_SUCCESS(rv, rv); - - // We're done with the initialization now and can skip the remaining - // upgrading logic. - return NS_OK; - } - - // Checking the database schema now - int32_t schemaVersion; - rv = mDBConn->GetSchemaVersion(&schemaVersion); + int32_t val; + rv = prefBranch->GetIntPref(NS_PREF_FOLDERLIST, + &val); NS_ENSURE_SUCCESS(rv, rv); - // Changing the database? Be sure to do these two things! - // 1) Increment DM_SCHEMA_VERSION - // 2) Implement the proper downgrade/upgrade code for the current version + switch(val) { + case 0: // Desktop + { + nsCOMPtr downloadDir; + rv = dirService->Get(NS_OS_DESKTOP_DIR, + NS_GET_IID(nsIFile), + getter_AddRefs(downloadDir)); + NS_ENSURE_SUCCESS(rv, rv); + downloadDir.forget(aResult); + return NS_OK; + } + break; + case 1: // Downloads + return GetDefaultDownloadsDirectory(aResult); + case 2: // Custom + { + nsCOMPtr customDirectory; + prefBranch->GetComplexValue(NS_PREF_DIR, + NS_GET_IID(nsIFile), + getter_AddRefs(customDirectory)); + if (customDirectory) { + bool exists = false; + (void)customDirectory->Exists(&exists); - switch (schemaVersion) { - // Upgrading - // Every time you increment the database schema, you need to implement - // the upgrading code from the previous version to the new one. - // Also, don't forget to make a unit test to test your upgrading code! - case 1: // Drop a column (iconURL) from the database (bug 385875) - { - // Safely wrap this in a transaction so we don't hose the whole DB - mozStorageTransaction safeTransaction(mDBConn, true); + if (!exists) { + rv = customDirectory->Create(nsIFile::DIRECTORY_TYPE, 0755); + if (NS_SUCCEEDED(rv)) { + customDirectory.forget(aResult); + return NS_OK; + } - // Create a temporary table that will store the existing records - rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE TEMPORARY TABLE moz_downloads_backup (" - "id INTEGER PRIMARY KEY, " - "name TEXT, " - "source TEXT, " - "target TEXT, " - "startTime INTEGER, " - "endTime INTEGER, " - "state INTEGER" - ")")); - NS_ENSURE_SUCCESS(rv, rv); + // Create failed, so it still doesn't exist. Fall out and get the + // default downloads directory. + } - // Insert into a temporary table - rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "INSERT INTO moz_downloads_backup " - "SELECT id, name, source, target, startTime, endTime, state " - "FROM moz_downloads")); - NS_ENSURE_SUCCESS(rv, rv); + bool writable = false; + bool directory = false; + (void)customDirectory->IsWritable(&writable); + (void)customDirectory->IsDirectory(&directory); - // Drop the old table - rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "DROP TABLE moz_downloads")); - NS_ENSURE_SUCCESS(rv, rv); - - // Now recreate it with this schema version - rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE TABLE moz_downloads (" - "id INTEGER PRIMARY KEY, " - "name TEXT, " - "source TEXT, " - "target TEXT, " - "startTime INTEGER, " - "endTime INTEGER, " - "state INTEGER" - ")")); - NS_ENSURE_SUCCESS(rv, rv); - - // Insert the data back into it - rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "INSERT INTO moz_downloads " - "SELECT id, name, source, target, startTime, endTime, state " - "FROM moz_downloads_backup")); - NS_ENSURE_SUCCESS(rv, rv); - - // And drop our temporary table - rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "DROP TABLE moz_downloads_backup")); - NS_ENSURE_SUCCESS(rv, rv); - - // Finally, update the schemaVersion variable and the database schema - schemaVersion = 2; - rv = mDBConn->SetSchemaVersion(schemaVersion); - NS_ENSURE_SUCCESS(rv, rv); - } - // Fallthrough to the next upgrade - MOZ_FALLTHROUGH; - - case 2: // Add referrer column to the database - { - rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "ALTER TABLE moz_downloads " - "ADD COLUMN referrer TEXT")); - NS_ENSURE_SUCCESS(rv, rv); - - // Finally, update the schemaVersion variable and the database schema - schemaVersion = 3; - rv = mDBConn->SetSchemaVersion(schemaVersion); - NS_ENSURE_SUCCESS(rv, rv); - } - // Fallthrough to the next upgrade - MOZ_FALLTHROUGH; - - case 3: // This version adds a column to the database (entityID) - { - rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "ALTER TABLE moz_downloads " - "ADD COLUMN entityID TEXT")); - NS_ENSURE_SUCCESS(rv, rv); - - // Finally, update the schemaVersion variable and the database schema - schemaVersion = 4; - rv = mDBConn->SetSchemaVersion(schemaVersion); - NS_ENSURE_SUCCESS(rv, rv); - } - // Fallthrough to the next upgrade - MOZ_FALLTHROUGH; - - case 4: // This version adds a column to the database (tempPath) - { - rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "ALTER TABLE moz_downloads " - "ADD COLUMN tempPath TEXT")); - NS_ENSURE_SUCCESS(rv, rv); - - // Finally, update the schemaVersion variable and the database schema - schemaVersion = 5; - rv = mDBConn->SetSchemaVersion(schemaVersion); - NS_ENSURE_SUCCESS(rv, rv); - } - // Fallthrough to the next upgrade - MOZ_FALLTHROUGH; - - case 5: // This version adds two columns for tracking transfer progress - { - rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "ALTER TABLE moz_downloads " - "ADD COLUMN currBytes INTEGER NOT NULL DEFAULT 0")); - NS_ENSURE_SUCCESS(rv, rv); - - rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "ALTER TABLE moz_downloads " - "ADD COLUMN maxBytes INTEGER NOT NULL DEFAULT -1")); - NS_ENSURE_SUCCESS(rv, rv); - - // Finally, update the schemaVersion variable and the database schema - schemaVersion = 6; - rv = mDBConn->SetSchemaVersion(schemaVersion); - NS_ENSURE_SUCCESS(rv, rv); - } - // Fallthrough to the next upgrade - MOZ_FALLTHROUGH; - - case 6: // This version adds three columns to DB (MIME type related info) - { - rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "ALTER TABLE moz_downloads " - "ADD COLUMN mimeType TEXT")); - NS_ENSURE_SUCCESS(rv, rv); - - rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "ALTER TABLE moz_downloads " - "ADD COLUMN preferredApplication TEXT")); - NS_ENSURE_SUCCESS(rv, rv); - - rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "ALTER TABLE moz_downloads " - "ADD COLUMN preferredAction INTEGER NOT NULL DEFAULT 0")); - NS_ENSURE_SUCCESS(rv, rv); - - // Finally, update the schemaVersion variable and the database schema - schemaVersion = 7; - rv = mDBConn->SetSchemaVersion(schemaVersion); - NS_ENSURE_SUCCESS(rv, rv); - } - // Fallthrough to next upgrade - MOZ_FALLTHROUGH; - - case 7: // This version adds a column to remember to auto-resume downloads - { - rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "ALTER TABLE moz_downloads " - "ADD COLUMN autoResume INTEGER NOT NULL DEFAULT 0")); - NS_ENSURE_SUCCESS(rv, rv); - - // Finally, update the schemaVersion variable and the database schema - schemaVersion = 8; - rv = mDBConn->SetSchemaVersion(schemaVersion); - NS_ENSURE_SUCCESS(rv, rv); - } - // Fallthrough to the next upgrade - MOZ_FALLTHROUGH; - - // Warning: schema versions >=8 must take into account that they can - // be operating on schemas from unknown, future versions that have - // been downgraded. Operations such as adding columns may fail, - // since the column may already exist. - - case 8: // This version adds a column for GUIDs - { - bool exists; - rv = mDBConn->IndexExists(NS_LITERAL_CSTRING("moz_downloads_guid_uniqueindex"), - &exists); - NS_ENSURE_SUCCESS(rv, rv); - if (!exists) { - rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "ALTER TABLE moz_downloads ADD COLUMN guid TEXT")); - NS_ENSURE_SUCCESS(rv, rv); - rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE UNIQUE INDEX moz_downloads_guid_uniqueindex ON moz_downloads (guid)")); - NS_ENSURE_SUCCESS(rv, rv); - } - - rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "UPDATE moz_downloads SET guid = GENERATE_GUID() WHERE guid ISNULL")); - NS_ENSURE_SUCCESS(rv, rv); - - // Finally, update the database schema - schemaVersion = 9; - rv = mDBConn->SetSchemaVersion(schemaVersion); - NS_ENSURE_SUCCESS(rv, rv); - } - // Fallthrough to the next upgrade - - // Extra sanity checking for developers -#ifndef DEBUG - MOZ_FALLTHROUGH; - case DM_SCHEMA_VERSION: -#endif - break; - - case 0: - { - NS_WARNING("Could not get download database's schema version!"); - - // The table may still be usable - someone may have just messed with the - // schema version, so let's just treat this like a downgrade and verify - // that the needed columns are there. If they aren't there, we'll drop - // the table anyway. - rv = mDBConn->SetSchemaVersion(DM_SCHEMA_VERSION); - NS_ENSURE_SUCCESS(rv, rv); - } - // Fallthrough to downgrade check - MOZ_FALLTHROUGH; - - // Downgrading - // If columns have been added to the table, we can still use the ones we - // understand safely. If columns have been deleted or alterd, we just - // drop the table and start from scratch. If you change how a column - // should be interpreted, make sure you also change its name so this - // check will catch it. - default: - { - nsCOMPtr stmt; - rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING( - "SELECT id, name, source, target, tempPath, startTime, endTime, state, " - "referrer, entityID, currBytes, maxBytes, mimeType, " - "preferredApplication, preferredAction, autoResume, guid " - "FROM moz_downloads"), getter_AddRefs(stmt)); - if (NS_SUCCEEDED(rv)) { - // We have a database that contains all of the elements that make up - // the latest known schema. Reset the version to force an upgrade - // path if this downgraded database is used in a later version. - mDBConn->SetSchemaVersion(DM_SCHEMA_VERSION); - break; - } - - // if the statement fails, that means all the columns were not there. - // First we backup the database - nsCOMPtr storage = - do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID); - NS_ENSURE_TRUE(storage, NS_ERROR_NOT_AVAILABLE); - nsCOMPtr backup; - rv = storage->BackupDatabaseFile(dbFile, DM_DB_CORRUPT_FILENAME, nullptr, - getter_AddRefs(backup)); - NS_ENSURE_SUCCESS(rv, rv); - - // Then we dump it - rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "DROP TABLE moz_downloads")); - NS_ENSURE_SUCCESS(rv, rv); - - rv = CreateTable(mDBConn); - NS_ENSURE_SUCCESS(rv, rv); - } - break; - } - - return NS_OK; -} - -nsresult -nsDownloadManager::CreateTable(mozIStorageConnection* aDBConn) -{ - nsresult rv = aDBConn->SetSchemaVersion(DM_SCHEMA_VERSION); - if (NS_FAILED(rv)) return rv; - - rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE TABLE moz_downloads (" - "id INTEGER PRIMARY KEY, " - "name TEXT, " - "source TEXT, " - "target TEXT, " - "tempPath TEXT, " - "startTime INTEGER, " - "endTime INTEGER, " - "state INTEGER, " - "referrer TEXT, " - "entityID TEXT, " - "currBytes INTEGER NOT NULL DEFAULT 0, " - "maxBytes INTEGER NOT NULL DEFAULT -1, " - "mimeType TEXT, " - "preferredApplication TEXT, " - "preferredAction INTEGER NOT NULL DEFAULT 0, " - "autoResume INTEGER NOT NULL DEFAULT 0, " - "guid TEXT" - ")")); - if (NS_FAILED(rv)) return rv; - - rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE UNIQUE INDEX moz_downloads_guid_uniqueindex " - "ON moz_downloads(guid)")); - return rv; -} - -nsresult -nsDownloadManager::RestoreDatabaseState() -{ - // Restore downloads that were in a scanning state. We can assume that they - // have been dealt with by the virus scanner - nsCOMPtr stmt; - nsresult rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING( - "UPDATE moz_downloads " - "SET state = :state " - "WHERE state = :state_cond"), getter_AddRefs(stmt)); - NS_ENSURE_SUCCESS(rv, rv); - - rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("state"), nsIDownloadManager::DOWNLOAD_FINISHED); - NS_ENSURE_SUCCESS(rv, rv); - rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("state_cond"), nsIDownloadManager::DOWNLOAD_SCANNING); - NS_ENSURE_SUCCESS(rv, rv); - - rv = stmt->Execute(); - NS_ENSURE_SUCCESS(rv, rv); - - // Convert supposedly-active downloads into downloads that should auto-resume - rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING( - "UPDATE moz_downloads " - "SET autoResume = :autoResume " - "WHERE state = :notStarted " - "OR state = :queued " - "OR state = :downloading"), getter_AddRefs(stmt)); - NS_ENSURE_SUCCESS(rv, rv); - - rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("autoResume"), nsDownload::AUTO_RESUME); - NS_ENSURE_SUCCESS(rv, rv); - rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("notStarted"), nsIDownloadManager::DOWNLOAD_NOTSTARTED); - NS_ENSURE_SUCCESS(rv, rv); - rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("queued"), nsIDownloadManager::DOWNLOAD_QUEUED); - NS_ENSURE_SUCCESS(rv, rv); - rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("downloading"), nsIDownloadManager::DOWNLOAD_DOWNLOADING); - NS_ENSURE_SUCCESS(rv, rv); - - rv = stmt->Execute(); - NS_ENSURE_SUCCESS(rv, rv); - - // Switch any download that is supposed to automatically resume and is in a - // finished state to *not* automatically resume. See Bug 409179 for details. - rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING( - "UPDATE moz_downloads " - "SET autoResume = :autoResume " - "WHERE state = :state " - "AND autoResume = :autoResume_cond"), - getter_AddRefs(stmt)); - NS_ENSURE_SUCCESS(rv, rv); - - rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("autoResume"), nsDownload::DONT_RESUME); - NS_ENSURE_SUCCESS(rv, rv); - rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("state"), nsIDownloadManager::DOWNLOAD_FINISHED); - NS_ENSURE_SUCCESS(rv, rv); - rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("autoResume_cond"), nsDownload::AUTO_RESUME); - NS_ENSURE_SUCCESS(rv, rv); - - rv = stmt->Execute(); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; -} - -nsresult -nsDownloadManager::RestoreActiveDownloads() -{ - nsCOMPtr stmt; - nsresult rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING( - "SELECT id " - "FROM moz_downloads " - "WHERE (state = :state AND LENGTH(entityID) > 0) " - "OR autoResume != :autoResume"), getter_AddRefs(stmt)); - NS_ENSURE_SUCCESS(rv, rv); - - rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("state"), nsIDownloadManager::DOWNLOAD_PAUSED); - NS_ENSURE_SUCCESS(rv, rv); - rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("autoResume"), nsDownload::DONT_RESUME); - NS_ENSURE_SUCCESS(rv, rv); - - nsresult retVal = NS_OK; - bool hasResults; - while (NS_SUCCEEDED(stmt->ExecuteStep(&hasResults)) && hasResults) { - RefPtr dl; - // Keep trying to add even if we fail one, but make sure to return failure. - // Additionally, be careful to not call anything that tries to change the - // database because we're iterating over a live statement. - if (NS_FAILED(GetDownloadFromDB(stmt->AsInt32(0), getter_AddRefs(dl))) || - NS_FAILED(AddToCurrentDownloads(dl))) - retVal = NS_ERROR_FAILURE; - } - - // Try to resume only the downloads that should auto-resume - rv = ResumeAllDownloads(false); - NS_ENSURE_SUCCESS(rv, rv); - - return retVal; -} - -int64_t -nsDownloadManager::AddDownloadToDB(const nsAString &aName, - const nsACString &aSource, - const nsACString &aTarget, - const nsAString &aTempPath, - int64_t aStartTime, - int64_t aEndTime, - const nsACString &aMimeType, - const nsACString &aPreferredApp, - nsHandlerInfoAction aPreferredAction, - bool aPrivate, - nsACString& aNewGUID) -{ - mozIStorageConnection* dbConn = aPrivate ? mPrivateDBConn : mDBConn; - nsCOMPtr stmt; - nsresult rv = dbConn->CreateStatement(NS_LITERAL_CSTRING( - "INSERT INTO moz_downloads " - "(name, source, target, tempPath, startTime, endTime, state, " - "mimeType, preferredApplication, preferredAction, guid) VALUES " - "(:name, :source, :target, :tempPath, :startTime, :endTime, :state, " - ":mimeType, :preferredApplication, :preferredAction, :guid)"), - getter_AddRefs(stmt)); - NS_ENSURE_SUCCESS(rv, 0); - - rv = stmt->BindStringByName(NS_LITERAL_CSTRING("name"), aName); - NS_ENSURE_SUCCESS(rv, 0); - - rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("source"), aSource); - NS_ENSURE_SUCCESS(rv, 0); - - rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("target"), aTarget); - NS_ENSURE_SUCCESS(rv, 0); - - rv = stmt->BindStringByName(NS_LITERAL_CSTRING("tempPath"), aTempPath); - NS_ENSURE_SUCCESS(rv, 0); - - rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("startTime"), aStartTime); - NS_ENSURE_SUCCESS(rv, 0); - - rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("endTime"), aEndTime); - NS_ENSURE_SUCCESS(rv, 0); - - rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("state"), nsIDownloadManager::DOWNLOAD_NOTSTARTED); - NS_ENSURE_SUCCESS(rv, 0); - - rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("mimeType"), aMimeType); - NS_ENSURE_SUCCESS(rv, 0); - - rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("preferredApplication"), aPreferredApp); - NS_ENSURE_SUCCESS(rv, 0); - - rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("preferredAction"), aPreferredAction); - NS_ENSURE_SUCCESS(rv, 0); - - nsAutoCString guid; - rv = GenerateGUID(guid); - NS_ENSURE_SUCCESS(rv, 0); - rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("guid"), guid); - NS_ENSURE_SUCCESS(rv, 0); - - bool hasMore; - rv = stmt->ExecuteStep(&hasMore); // we want to keep our lock - NS_ENSURE_SUCCESS(rv, 0); - - int64_t id = 0; - rv = dbConn->GetLastInsertRowID(&id); - NS_ENSURE_SUCCESS(rv, 0); - - aNewGUID = guid; - - // lock on DB from statement will be released once we return - return id; -} - -nsresult -nsDownloadManager::InitDB() -{ - nsresult rv = InitPrivateDB(); - NS_ENSURE_SUCCESS(rv, rv); - - rv = InitFileDB(); - NS_ENSURE_SUCCESS(rv, rv); - - rv = InitStatements(mDBConn, getter_AddRefs(mUpdateDownloadStatement), - getter_AddRefs(mGetIdsForURIStatement)); - NS_ENSURE_SUCCESS(rv, rv); - return NS_OK; -} - -nsresult -nsDownloadManager::InitStatements(mozIStorageConnection* aDBConn, - mozIStorageStatement** aUpdateStatement, - mozIStorageStatement** aGetIdsStatement) -{ - nsresult rv = aDBConn->CreateStatement(NS_LITERAL_CSTRING( - "UPDATE moz_downloads " - "SET tempPath = :tempPath, startTime = :startTime, endTime = :endTime, " - "state = :state, referrer = :referrer, entityID = :entityID, " - "currBytes = :currBytes, maxBytes = :maxBytes, autoResume = :autoResume " - "WHERE id = :id"), aUpdateStatement); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aDBConn->CreateStatement(NS_LITERAL_CSTRING( - "SELECT guid " - "FROM moz_downloads " - "WHERE source = :source"), aGetIdsStatement); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; -} - -nsresult -nsDownloadManager::Init() -{ - nsresult rv; - - nsCOMPtr bundleService = - mozilla::services::GetStringBundleService(); - if (!bundleService) - return NS_ERROR_FAILURE; - - rv = bundleService->CreateBundle(DOWNLOAD_MANAGER_BUNDLE, - getter_AddRefs(mBundle)); - NS_ENSURE_SUCCESS(rv, rv); - - mUseJSTransfer = true; - - if (mUseJSTransfer) - return NS_OK; - - // Clean up any old downloads.rdf files from before Firefox 3 - { - nsCOMPtr oldDownloadsFile; - bool fileExists; - if (NS_SUCCEEDED(NS_GetSpecialDirectory(NS_APP_DOWNLOADS_50_FILE, - getter_AddRefs(oldDownloadsFile))) && - NS_SUCCEEDED(oldDownloadsFile->Exists(&fileExists)) && - fileExists) { - (void)oldDownloadsFile->Remove(false); - } - } - - mObserverService = mozilla::services::GetObserverService(); - if (!mObserverService) - return NS_ERROR_FAILURE; - - rv = InitDB(); - NS_ENSURE_SUCCESS(rv, rv); - -#ifdef DOWNLOAD_SCANNER - mScanner = new nsDownloadScanner(); - if (!mScanner) - return NS_ERROR_OUT_OF_MEMORY; - rv = mScanner->Init(); - if (NS_FAILED(rv)) { - delete mScanner; - mScanner = nullptr; - } -#endif - - // Do things *after* initializing various download manager properties such as - // restoring downloads to a consistent state - rv = RestoreDatabaseState(); - NS_ENSURE_SUCCESS(rv, rv); - - rv = RestoreActiveDownloads(); - NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), - "Failed to restore all active downloads"); - - nsCOMPtr history = - do_GetService(NS_NAVHISTORYSERVICE_CONTRACTID); - - (void)mObserverService->NotifyObservers( - static_cast(this), - "download-manager-initialized", - nullptr); - - // The following AddObserver calls must be the last lines in this function, - // because otherwise, this function may fail (and thus, this object would be not - // completely initialized), but the observerservice would still keep a reference - // to us and notify us about shutdown, which may cause crashes. - // failure to add an observer is not critical - (void)mObserverService->AddObserver(this, "quit-application", true); - (void)mObserverService->AddObserver(this, "quit-application-requested", true); - (void)mObserverService->AddObserver(this, "offline-requested", true); - (void)mObserverService->AddObserver(this, "sleep_notification", true); - (void)mObserverService->AddObserver(this, "wake_notification", true); - (void)mObserverService->AddObserver(this, "suspend_process_notification", true); - (void)mObserverService->AddObserver(this, "resume_process_notification", true); - (void)mObserverService->AddObserver(this, "profile-before-change", true); - (void)mObserverService->AddObserver(this, NS_IOSERVICE_GOING_OFFLINE_TOPIC, true); - (void)mObserverService->AddObserver(this, NS_IOSERVICE_OFFLINE_STATUS_TOPIC, true); - (void)mObserverService->AddObserver(this, "last-pb-context-exited", true); - (void)mObserverService->AddObserver(this, "last-pb-context-exiting", true); - - if (history) - (void)history->AddObserver(this, true); - - return NS_OK; -} - -int32_t -nsDownloadManager::GetRetentionBehavior() -{ - // We use 0 as the default, which is "remove when done" - nsresult rv; - nsCOMPtr pref = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv); - NS_ENSURE_SUCCESS(rv, 0); - - int32_t val; - rv = pref->GetIntPref(PREF_BDM_RETENTION, &val); - NS_ENSURE_SUCCESS(rv, 0); - - // Allow the Downloads Panel to change the retention behavior. We do this to - // allow proper migration to the new feature when using the same profile on - // multiple versions of the product (bug 697678). Implementation note: in - // order to allow observers to change the retention value, we have to pass an - // object in the aSubject parameter, we cannot use aData for that. - nsCOMPtr retentionBehavior = - do_CreateInstance(NS_SUPPORTS_PRINT32_CONTRACTID); - retentionBehavior->SetData(val); - (void)mObserverService->NotifyObservers(retentionBehavior, - "download-manager-change-retention", - nullptr); - retentionBehavior->GetData(&val); - - return val; -} - -enum nsDownloadManager::QuitBehavior -nsDownloadManager::GetQuitBehavior() -{ - // We use 0 as the default, which is "remember and resume the download" - nsresult rv; - nsCOMPtr pref = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv); - NS_ENSURE_SUCCESS(rv, QUIT_AND_RESUME); - - int32_t val; - rv = pref->GetIntPref(PREF_BDM_QUITBEHAVIOR, &val); - NS_ENSURE_SUCCESS(rv, QUIT_AND_RESUME); - - switch (val) { - case 1: - return QUIT_AND_PAUSE; - case 2: - return QUIT_AND_CANCEL; - default: - return QUIT_AND_RESUME; - } -} - -// Using a globally-unique GUID, search all databases (both private and public). -// A return value of NS_ERROR_NOT_AVAILABLE means no download with the given GUID -// could be found, either private or public. - -nsresult -nsDownloadManager::GetDownloadFromDB(const nsACString& aGUID, nsDownload **retVal) -{ - MOZ_ASSERT(!FindDownload(aGUID), - "If it is a current download, you should not call this method!"); - - NS_NAMED_LITERAL_CSTRING(query, - "SELECT id, state, startTime, source, target, tempPath, name, referrer, " - "entityID, currBytes, maxBytes, mimeType, preferredAction, " - "preferredApplication, autoResume, guid " - "FROM moz_downloads " - "WHERE guid = :guid"); - // First, let's query the database and see if it even exists - nsCOMPtr stmt; - nsresult rv = mDBConn->CreateStatement(query, getter_AddRefs(stmt)); - NS_ENSURE_SUCCESS(rv, rv); - - rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("guid"), aGUID); - NS_ENSURE_SUCCESS(rv, rv); - - rv = GetDownloadFromDB(mDBConn, stmt, retVal); - - // If the download cannot be found in the public database, try again - // in the private one. Otherwise, return whatever successful result - // or failure obtained from the public database. - if (rv == NS_ERROR_NOT_AVAILABLE) { - rv = mPrivateDBConn->CreateStatement(query, getter_AddRefs(stmt)); - NS_ENSURE_SUCCESS(rv, rv); - - rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("guid"), aGUID); - NS_ENSURE_SUCCESS(rv, rv); - - rv = GetDownloadFromDB(mPrivateDBConn, stmt, retVal); - - // Only if it still cannot be found do we report the failure. - if (rv == NS_ERROR_NOT_AVAILABLE) { - *retVal = nullptr; - } - } - return rv; -} - -nsresult -nsDownloadManager::GetDownloadFromDB(uint32_t aID, nsDownload **retVal) -{ - NS_WARNING("Using integer IDs without compat mode enabled"); - - MOZ_ASSERT(!FindDownload(aID), - "If it is a current download, you should not call this method!"); - - // First, let's query the database and see if it even exists - nsCOMPtr stmt; - nsresult rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING( - "SELECT id, state, startTime, source, target, tempPath, name, referrer, " - "entityID, currBytes, maxBytes, mimeType, preferredAction, " - "preferredApplication, autoResume, guid " - "FROM moz_downloads " - "WHERE id = :id"), getter_AddRefs(stmt)); - NS_ENSURE_SUCCESS(rv, rv); - - rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("id"), aID); - NS_ENSURE_SUCCESS(rv, rv); - - return GetDownloadFromDB(mDBConn, stmt, retVal); -} - -nsresult -nsDownloadManager::GetDownloadFromDB(mozIStorageConnection* aDBConn, - mozIStorageStatement* stmt, - nsDownload **retVal) -{ - bool hasResults = false; - nsresult rv = stmt->ExecuteStep(&hasResults); - if (NS_FAILED(rv) || !hasResults) - return NS_ERROR_NOT_AVAILABLE; - - // We have a download, so lets create it - RefPtr dl = new nsDownload(); - if (!dl) - return NS_ERROR_OUT_OF_MEMORY; - dl->mPrivate = aDBConn == mPrivateDBConn; - - dl->mDownloadManager = this; - - int32_t i = 0; - // Setting all properties of the download now - dl->mCancelable = nullptr; - dl->mID = stmt->AsInt64(i++); - dl->mDownloadState = stmt->AsInt32(i++); - dl->mStartTime = stmt->AsInt64(i++); - - nsCString source; - stmt->GetUTF8String(i++, source); - rv = NS_NewURI(getter_AddRefs(dl->mSource), source); - NS_ENSURE_SUCCESS(rv, rv); - - nsCString target; - stmt->GetUTF8String(i++, target); - rv = NS_NewURI(getter_AddRefs(dl->mTarget), target); - NS_ENSURE_SUCCESS(rv, rv); - - nsString tempPath; - stmt->GetString(i++, tempPath); - if (!tempPath.IsEmpty()) { - rv = NS_NewLocalFile(tempPath, true, getter_AddRefs(dl->mTempFile)); - NS_ENSURE_SUCCESS(rv, rv); - } - - stmt->GetString(i++, dl->mDisplayName); - - nsCString referrer; - rv = stmt->GetUTF8String(i++, referrer); - if (NS_SUCCEEDED(rv) && !referrer.IsEmpty()) { - rv = NS_NewURI(getter_AddRefs(dl->mReferrer), referrer); - NS_ENSURE_SUCCESS(rv, rv); - } - - rv = stmt->GetUTF8String(i++, dl->mEntityID); - NS_ENSURE_SUCCESS(rv, rv); - - int64_t currBytes = stmt->AsInt64(i++); - int64_t maxBytes = stmt->AsInt64(i++); - dl->SetProgressBytes(currBytes, maxBytes); - - // Build mMIMEInfo only if the mimeType in DB is not empty - nsAutoCString mimeType; - rv = stmt->GetUTF8String(i++, mimeType); - NS_ENSURE_SUCCESS(rv, rv); - - if (!mimeType.IsEmpty()) { - nsCOMPtr mimeService = - do_GetService(NS_MIMESERVICE_CONTRACTID, &rv); - NS_ENSURE_SUCCESS(rv, rv); - - rv = mimeService->GetFromTypeAndExtension(mimeType, EmptyCString(), - getter_AddRefs(dl->mMIMEInfo)); - NS_ENSURE_SUCCESS(rv, rv); - - nsHandlerInfoAction action = stmt->AsInt32(i++); - rv = dl->mMIMEInfo->SetPreferredAction(action); - NS_ENSURE_SUCCESS(rv, rv); - - nsAutoCString persistentDescriptor; - rv = stmt->GetUTF8String(i++, persistentDescriptor); - NS_ENSURE_SUCCESS(rv, rv); - - if (!persistentDescriptor.IsEmpty()) { - nsCOMPtr handler = - do_CreateInstance(NS_LOCALHANDLERAPP_CONTRACTID, &rv); - NS_ENSURE_SUCCESS(rv, rv); - - nsCOMPtr localExecutable; - rv = NS_NewNativeLocalFile(EmptyCString(), false, - getter_AddRefs(localExecutable)); - NS_ENSURE_SUCCESS(rv, rv); - - rv = localExecutable->SetPersistentDescriptor(persistentDescriptor); - NS_ENSURE_SUCCESS(rv, rv); - - rv = handler->SetExecutable(localExecutable); - NS_ENSURE_SUCCESS(rv, rv); - - rv = dl->mMIMEInfo->SetPreferredApplicationHandler(handler); - NS_ENSURE_SUCCESS(rv, rv); - } - } else { - // Compensate for the i++s skipped in the true block - i += 2; - } - - dl->mAutoResume = - static_cast(stmt->AsInt32(i++)); - - rv = stmt->GetUTF8String(i++, dl->mGUID); - NS_ENSURE_SUCCESS(rv, rv); - - // Handle situations where we load a download from a database that has been - // used in an older version and not gone through the upgrade path (ie. it - // contains empty GUID entries). - if (dl->mGUID.IsEmpty()) { - rv = GenerateGUID(dl->mGUID); - NS_ENSURE_SUCCESS(rv, rv); - nsCOMPtr updateStmt; - rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING( - "UPDATE moz_downloads SET guid = :guid " - "WHERE id = :id"), - getter_AddRefs(updateStmt)); - NS_ENSURE_SUCCESS(rv, rv); - rv = updateStmt->BindUTF8StringByName(NS_LITERAL_CSTRING("guid"), dl->mGUID); - NS_ENSURE_SUCCESS(rv, rv); - rv = updateStmt->BindInt64ByName(NS_LITERAL_CSTRING("id"), dl->mID); - NS_ENSURE_SUCCESS(rv, rv); - rv = updateStmt->Execute(); - NS_ENSURE_SUCCESS(rv, rv); - } - - // Addrefing and returning - dl.forget(retVal); - return NS_OK; -} - -nsresult -nsDownloadManager::AddToCurrentDownloads(nsDownload *aDl) -{ - nsCOMArray& currentDownloads = - aDl->mPrivate ? mCurrentPrivateDownloads : mCurrentDownloads; - if (!currentDownloads.AppendObject(aDl)) - return NS_ERROR_OUT_OF_MEMORY; - - aDl->mDownloadManager = this; - return NS_OK; -} - -void -nsDownloadManager::SendEvent(nsDownload *aDownload, const char *aTopic) -{ - (void)mObserverService->NotifyObservers(aDownload, aTopic, nullptr); -} - -//////////////////////////////////////////////////////////////////////////////// -//// nsIDownloadManager - -NS_IMETHODIMP -nsDownloadManager::GetActivePrivateDownloadCount(int32_t* aResult) -{ - NS_ENSURE_STATE(!mUseJSTransfer); - - *aResult = mCurrentPrivateDownloads.Count(); - return NS_OK; -} - -NS_IMETHODIMP -nsDownloadManager::GetActiveDownloadCount(int32_t *aResult) -{ - NS_ENSURE_STATE(!mUseJSTransfer); - - *aResult = mCurrentDownloads.Count(); - - return NS_OK; -} - -NS_IMETHODIMP -nsDownloadManager::GetActiveDownloads(nsISimpleEnumerator **aResult) -{ - NS_ENSURE_STATE(!mUseJSTransfer); - - return NS_NewArrayEnumerator(aResult, mCurrentDownloads); -} - -NS_IMETHODIMP -nsDownloadManager::GetActivePrivateDownloads(nsISimpleEnumerator **aResult) -{ - NS_ENSURE_STATE(!mUseJSTransfer); - - return NS_NewArrayEnumerator(aResult, mCurrentPrivateDownloads); -} - -/** - * For platforms where helper apps use the downloads directory (i.e. mobile), - * this should be kept in sync with nsExternalHelperAppService.cpp - */ -NS_IMETHODIMP -nsDownloadManager::GetDefaultDownloadsDirectory(nsIFile **aResult) -{ - nsCOMPtr downloadDir; - - nsresult rv; - nsCOMPtr dirService = - do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv); - NS_ENSURE_SUCCESS(rv, rv); - - // OSX 10.4: - // Desktop - // OSX 10.5: - // User download directory - // Vista: - // Downloads - // XP/2K: - // My Documents/Downloads - // Linux: - // XDG user dir spec, with a fallback to Home/Downloads - - nsXPIDLString folderName; - mBundle->GetStringFromName(u"downloadsFolder", - getter_Copies(folderName)); - -#if defined (XP_MACOSX) - rv = dirService->Get(NS_OSX_DEFAULT_DOWNLOAD_DIR, - NS_GET_IID(nsIFile), - getter_AddRefs(downloadDir)); - NS_ENSURE_SUCCESS(rv, rv); -#elif defined(XP_WIN) - rv = dirService->Get(NS_WIN_DEFAULT_DOWNLOAD_DIR, - NS_GET_IID(nsIFile), - getter_AddRefs(downloadDir)); - NS_ENSURE_SUCCESS(rv, rv); - - // Check the os version - nsCOMPtr infoService = - do_GetService(NS_SYSTEMINFO_CONTRACTID, &rv); - NS_ENSURE_SUCCESS(rv, rv); - - int32_t version; - NS_NAMED_LITERAL_STRING(osVersion, "version"); - rv = infoService->GetPropertyAsInt32(osVersion, &version); - NS_ENSURE_SUCCESS(rv, rv); - if (version < 6) { // XP/2K - // First get "My Documents" - rv = dirService->Get(NS_WIN_PERSONAL_DIR, - NS_GET_IID(nsIFile), - getter_AddRefs(downloadDir)); - NS_ENSURE_SUCCESS(rv, rv); - - rv = downloadDir->Append(folderName); - NS_ENSURE_SUCCESS(rv, rv); - - // This could be the first time we are creating the downloads folder in My - // Documents, so make sure it exists. - bool exists; - rv = downloadDir->Exists(&exists); - NS_ENSURE_SUCCESS(rv, rv); - if (!exists) { - rv = downloadDir->Create(nsIFile::DIRECTORY_TYPE, 0755); - NS_ENSURE_SUCCESS(rv, rv); - } - } -#elif defined(XP_UNIX) -#if defined(MOZ_WIDGET_ANDROID) - // Android doesn't have a $HOME directory, and by default we only have - // write access to /data/data/org.mozilla.{$APP} and /sdcard - char* downloadDirPath = getenv("DOWNLOADS_DIRECTORY"); - if (downloadDirPath) { - rv = NS_NewNativeLocalFile(nsDependentCString(downloadDirPath), - true, getter_AddRefs(downloadDir)); - NS_ENSURE_SUCCESS(rv, rv); - } - else { - rv = NS_ERROR_FAILURE; - } -#else - rv = dirService->Get(NS_UNIX_DEFAULT_DOWNLOAD_DIR, - NS_GET_IID(nsIFile), - getter_AddRefs(downloadDir)); - // fallback to Home/Downloads - if (NS_FAILED(rv)) { - rv = dirService->Get(NS_UNIX_HOME_DIR, - NS_GET_IID(nsIFile), - getter_AddRefs(downloadDir)); - NS_ENSURE_SUCCESS(rv, rv); - rv = downloadDir->Append(folderName); - NS_ENSURE_SUCCESS(rv, rv); - } -#endif -#else - rv = dirService->Get(NS_OS_HOME_DIR, - NS_GET_IID(nsIFile), - getter_AddRefs(downloadDir)); - NS_ENSURE_SUCCESS(rv, rv); - rv = downloadDir->Append(folderName); - NS_ENSURE_SUCCESS(rv, rv); -#endif - - downloadDir.forget(aResult); - - return NS_OK; -} - -#define NS_BRANCH_DOWNLOAD "browser.download." -#define NS_PREF_FOLDERLIST "folderList" -#define NS_PREF_DIR "dir" - -NS_IMETHODIMP -nsDownloadManager::GetUserDownloadsDirectory(nsIFile **aResult) -{ - nsresult rv; - nsCOMPtr dirService = - do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv); - NS_ENSURE_SUCCESS(rv, rv); - - nsCOMPtr prefService = - do_GetService(NS_PREFSERVICE_CONTRACTID, &rv); - NS_ENSURE_SUCCESS(rv, rv); - - nsCOMPtr prefBranch; - rv = prefService->GetBranch(NS_BRANCH_DOWNLOAD, - getter_AddRefs(prefBranch)); - NS_ENSURE_SUCCESS(rv, rv); - - int32_t val; - rv = prefBranch->GetIntPref(NS_PREF_FOLDERLIST, - &val); - NS_ENSURE_SUCCESS(rv, rv); - - switch(val) { - case 0: // Desktop - { - nsCOMPtr downloadDir; - rv = dirService->Get(NS_OS_DESKTOP_DIR, - NS_GET_IID(nsIFile), - getter_AddRefs(downloadDir)); - NS_ENSURE_SUCCESS(rv, rv); - downloadDir.forget(aResult); - return NS_OK; - } - break; - case 1: // Downloads - return GetDefaultDownloadsDirectory(aResult); - case 2: // Custom - { - nsCOMPtr customDirectory; - prefBranch->GetComplexValue(NS_PREF_DIR, - NS_GET_IID(nsIFile), - getter_AddRefs(customDirectory)); - if (customDirectory) { - bool exists = false; - (void)customDirectory->Exists(&exists); - - if (!exists) { - rv = customDirectory->Create(nsIFile::DIRECTORY_TYPE, 0755); - if (NS_SUCCEEDED(rv)) { - customDirectory.forget(aResult); - return NS_OK; - } - - // Create failed, so it still doesn't exist. Fall out and get the - // default downloads directory. - } - - bool writable = false; - bool directory = false; - (void)customDirectory->IsWritable(&writable); - (void)customDirectory->IsDirectory(&directory); - - if (exists && writable && directory) { - customDirectory.forget(aResult); - return NS_OK; - } - } - rv = GetDefaultDownloadsDirectory(aResult); - if (NS_SUCCEEDED(rv)) { - (void)prefBranch->SetComplexValue(NS_PREF_DIR, - NS_GET_IID(nsIFile), - *aResult); - } - return rv; - } - break; - } - return NS_ERROR_INVALID_ARG; -} - -NS_IMETHODIMP -nsDownloadManager::AddDownload(DownloadType aDownloadType, - nsIURI *aSource, - nsIURI *aTarget, - const nsAString& aDisplayName, - nsIMIMEInfo *aMIMEInfo, - PRTime aStartTime, - nsIFile *aTempFile, - nsICancelable *aCancelable, - bool aIsPrivate, - nsIDownload **aDownload) -{ - NS_ENSURE_STATE(!mUseJSTransfer); - - NS_ENSURE_ARG_POINTER(aSource); - NS_ENSURE_ARG_POINTER(aTarget); - NS_ENSURE_ARG_POINTER(aDownload); - - nsresult rv; - - // target must be on the local filesystem - nsCOMPtr targetFileURL = do_QueryInterface(aTarget, &rv); - NS_ENSURE_SUCCESS(rv, rv); - - nsCOMPtr targetFile; - rv = targetFileURL->GetFile(getter_AddRefs(targetFile)); - NS_ENSURE_SUCCESS(rv, rv); - - RefPtr dl = new nsDownload(); - if (!dl) - return NS_ERROR_OUT_OF_MEMORY; - - // give our new nsIDownload some info so it's ready to go off into the world - dl->mTarget = aTarget; - dl->mSource = aSource; - dl->mTempFile = aTempFile; - dl->mPrivate = aIsPrivate; - - dl->mDisplayName = aDisplayName; - if (dl->mDisplayName.IsEmpty()) - targetFile->GetLeafName(dl->mDisplayName); - - dl->mMIMEInfo = aMIMEInfo; - dl->SetStartTime(aStartTime == 0 ? PR_Now() : aStartTime); - - // Creates a cycle that will be broken when the download finishes - dl->mCancelable = aCancelable; - - // Adding to the DB - nsAutoCString source, target; - rv = aSource->GetSpec(source); - NS_ENSURE_SUCCESS(rv, rv); - rv = aTarget->GetSpec(target); - NS_ENSURE_SUCCESS(rv, rv); - - // Track the temp file for exthandler downloads - nsAutoString tempPath; - if (aTempFile) - aTempFile->GetPath(tempPath); - - // Break down MIMEInfo but don't panic if we can't get all the pieces - we - // can still download the file - nsAutoCString persistentDescriptor, mimeType; - nsHandlerInfoAction action = nsIMIMEInfo::saveToDisk; - if (aMIMEInfo) { - (void)aMIMEInfo->GetType(mimeType); - - nsCOMPtr handlerApp; - (void)aMIMEInfo->GetPreferredApplicationHandler(getter_AddRefs(handlerApp)); - nsCOMPtr locHandlerApp = do_QueryInterface(handlerApp); - - if (locHandlerApp) { - nsCOMPtr executable; - (void)locHandlerApp->GetExecutable(getter_AddRefs(executable)); - Unused << executable->GetPersistentDescriptor(persistentDescriptor); - } - - (void)aMIMEInfo->GetPreferredAction(&action); - } - - int64_t id = AddDownloadToDB(dl->mDisplayName, source, target, tempPath, - dl->mStartTime, dl->mLastUpdate, - mimeType, persistentDescriptor, action, - dl->mPrivate, dl->mGUID /* outparam */); - NS_ENSURE_TRUE(id, NS_ERROR_FAILURE); - dl->mID = id; - - rv = AddToCurrentDownloads(dl); - (void)dl->SetState(nsIDownloadManager::DOWNLOAD_QUEUED); - NS_ENSURE_SUCCESS(rv, rv); - -#ifdef DOWNLOAD_SCANNER - if (mScanner) { - bool scan = true; - nsCOMPtr prefs(do_GetService(NS_PREFSERVICE_CONTRACTID)); - if (prefs) { - (void)prefs->GetBoolPref(PREF_BDM_SCANWHENDONE, &scan); - } - // We currently apply local security policy to downloads when we scan - // via windows all-in-one download security api. The CheckPolicy call - // below is a pre-emptive part of that process. So tie applying security - // zone policy settings when downloads are intiated to the same pref - // that triggers applying security zone policy settings after a download - // completes. (bug 504804) - if (scan) { - AVCheckPolicyState res = mScanner->CheckPolicy(aSource, aTarget); - if (res == AVPOLICY_BLOCKED) { - // This download will get deleted during a call to IAE's Save, - // so go ahead and mark it as blocked and avoid the download. - (void)CancelDownload(id); - (void)dl->SetState(nsIDownloadManager::DOWNLOAD_BLOCKED_POLICY); - } - } - } -#endif - - // Check with parental controls to see if file downloads - // are allowed for this user. If not allowed, cancel the - // download and mark its state as being blocked. - nsCOMPtr pc = - do_CreateInstance(NS_PARENTALCONTROLSSERVICE_CONTRACTID); - if (pc) { - bool enabled = false; - (void)pc->GetBlockFileDownloadsEnabled(&enabled); - if (enabled) { - (void)CancelDownload(id); - (void)dl->SetState(nsIDownloadManager::DOWNLOAD_BLOCKED_PARENTAL); - } - - // Log the event if required by pc settings. - bool logEnabled = false; - (void)pc->GetLoggingEnabled(&logEnabled); - if (logEnabled) { - (void)pc->Log(nsIParentalControlsService::ePCLog_FileDownload, - enabled, - aSource, - nullptr); - } - } - - dl.forget(aDownload); - - return NS_OK; -} - -NS_IMETHODIMP -nsDownloadManager::GetDownload(uint32_t aID, nsIDownload **aDownloadItem) -{ - NS_ENSURE_STATE(!mUseJSTransfer); - - NS_WARNING("Using integer IDs without compat mode enabled"); - - nsDownload *itm = FindDownload(aID); - - RefPtr dl; - if (!itm) { - nsresult rv = GetDownloadFromDB(aID, getter_AddRefs(dl)); - NS_ENSURE_SUCCESS(rv, rv); - - itm = dl.get(); - } - - NS_ADDREF(*aDownloadItem = itm); - - return NS_OK; -} - -namespace { -class AsyncResult : public Runnable -{ -public: - AsyncResult(nsresult aStatus, nsIDownload* aResult, - nsIDownloadManagerResult* aCallback) - : mStatus(aStatus), mResult(aResult), mCallback(aCallback) - { - } - - NS_IMETHOD Run() override - { - mCallback->HandleResult(mStatus, mResult); - return NS_OK; - } - -private: - nsresult mStatus; - nsCOMPtr mResult; - nsCOMPtr mCallback; -}; -} // namespace - -NS_IMETHODIMP -nsDownloadManager::GetDownloadByGUID(const nsACString& aGUID, - nsIDownloadManagerResult* aCallback) -{ - NS_ENSURE_STATE(!mUseJSTransfer); - - nsDownload *itm = FindDownload(aGUID); - - nsresult rv = NS_OK; - RefPtr dl; - if (!itm) { - rv = GetDownloadFromDB(aGUID, getter_AddRefs(dl)); - itm = dl.get(); - } - - RefPtr runnable = new AsyncResult(rv, itm, aCallback); - NS_DispatchToMainThread(runnable); - return NS_OK; -} - -nsDownload * -nsDownloadManager::FindDownload(uint32_t aID) -{ - // we shouldn't ever have many downloads, so we can loop over them - for (int32_t i = mCurrentDownloads.Count() - 1; i >= 0; --i) { - nsDownload *dl = mCurrentDownloads[i]; - if (dl->mID == aID) - return dl; - } - - return nullptr; -} - -nsDownload * -nsDownloadManager::FindDownload(const nsACString& aGUID) -{ - // we shouldn't ever have many downloads, so we can loop over them - for (int32_t i = mCurrentDownloads.Count() - 1; i >= 0; --i) { - nsDownload *dl = mCurrentDownloads[i]; - if (dl->mGUID == aGUID) - return dl; - } - - for (int32_t i = mCurrentPrivateDownloads.Count() - 1; i >= 0; --i) { - nsDownload *dl = mCurrentPrivateDownloads[i]; - if (dl->mGUID == aGUID) - return dl; - } - - return nullptr; -} - -NS_IMETHODIMP -nsDownloadManager::CancelDownload(uint32_t aID) -{ - NS_ENSURE_STATE(!mUseJSTransfer); - - NS_WARNING("Using integer IDs without compat mode enabled"); - - // We AddRef here so we don't lose access to member variables when we remove - RefPtr dl = FindDownload(aID); - - // if it's null, someone passed us a bad id. - if (!dl) - return NS_ERROR_FAILURE; - - return dl->Cancel(); -} - -nsresult -nsDownloadManager::RetryDownload(const nsACString& aGUID) -{ - RefPtr dl; - nsresult rv = GetDownloadFromDB(aGUID, getter_AddRefs(dl)); - NS_ENSURE_SUCCESS(rv, rv); - - return RetryDownload(dl); -} - -NS_IMETHODIMP -nsDownloadManager::RetryDownload(uint32_t aID) -{ - NS_ENSURE_STATE(!mUseJSTransfer); - - NS_WARNING("Using integer IDs without compat mode enabled"); - - RefPtr dl; - nsresult rv = GetDownloadFromDB(aID, getter_AddRefs(dl)); - NS_ENSURE_SUCCESS(rv, rv); - - return RetryDownload(dl); -} - -nsresult -nsDownloadManager::RetryDownload(nsDownload* dl) -{ - // if our download is not canceled or failed, we should fail - if (dl->mDownloadState != nsIDownloadManager::DOWNLOAD_FAILED && - dl->mDownloadState != nsIDownloadManager::DOWNLOAD_BLOCKED_PARENTAL && - dl->mDownloadState != nsIDownloadManager::DOWNLOAD_BLOCKED_POLICY && - dl->mDownloadState != nsIDownloadManager::DOWNLOAD_DIRTY && - dl->mDownloadState != nsIDownloadManager::DOWNLOAD_CANCELED) - return NS_ERROR_FAILURE; - - // If the download has failed and is resumable then we first try resuming it - nsresult rv; - if (dl->mDownloadState == nsIDownloadManager::DOWNLOAD_FAILED && dl->IsResumable()) { - rv = dl->Resume(); - if (NS_SUCCEEDED(rv)) - return rv; - } - - // reset time and download progress - dl->SetStartTime(PR_Now()); - dl->SetProgressBytes(0, -1); - - nsCOMPtr wbp = - do_CreateInstance("@mozilla.org/embedding/browser/nsWebBrowserPersist;1", &rv); - NS_ENSURE_SUCCESS(rv, rv); - - rv = wbp->SetPersistFlags(nsIWebBrowserPersist::PERSIST_FLAGS_REPLACE_EXISTING_FILES | - nsIWebBrowserPersist::PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION); - NS_ENSURE_SUCCESS(rv, rv); - - rv = AddToCurrentDownloads(dl); - NS_ENSURE_SUCCESS(rv, rv); - - rv = dl->SetState(nsIDownloadManager::DOWNLOAD_QUEUED); - NS_ENSURE_SUCCESS(rv, rv); - - // Creates a cycle that will be broken when the download finishes - dl->mCancelable = wbp; - (void)wbp->SetProgressListener(dl); - - // referrer policy can be anything since referrer is nullptr - rv = wbp->SavePrivacyAwareURI(dl->mSource, nullptr, - nullptr, mozilla::net::RP_Unset, - nullptr, nullptr, - dl->mTarget, dl->mPrivate); - if (NS_FAILED(rv)) { - dl->mCancelable = nullptr; - (void)wbp->SetProgressListener(nullptr); - return rv; - } - - return NS_OK; -} - -static nsresult -RemoveDownloadByGUID(const nsACString& aGUID, mozIStorageConnection* aDBConn) -{ - nsCOMPtr stmt; - nsresult rv = aDBConn->CreateStatement(NS_LITERAL_CSTRING( - "DELETE FROM moz_downloads " - "WHERE guid = :guid"), getter_AddRefs(stmt)); - NS_ENSURE_SUCCESS(rv, rv); - - rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("guid"), aGUID); - NS_ENSURE_SUCCESS(rv, rv); - - rv = stmt->Execute(); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; -} - -nsresult -nsDownloadManager::RemoveDownload(const nsACString& aGUID) -{ - RefPtr dl = FindDownload(aGUID); - MOZ_ASSERT(!dl, "Can't call RemoveDownload on a download in progress!"); - if (dl) - return NS_ERROR_FAILURE; - - nsresult rv = GetDownloadFromDB(aGUID, getter_AddRefs(dl)); - NS_ENSURE_SUCCESS(rv, rv); - - if (dl->mPrivate) { - RemoveDownloadByGUID(aGUID, mPrivateDBConn); - } else { - RemoveDownloadByGUID(aGUID, mDBConn); - } - - return NotifyDownloadRemoval(dl); -} - -NS_IMETHODIMP -nsDownloadManager::RemoveDownload(uint32_t aID) -{ - NS_ENSURE_STATE(!mUseJSTransfer); - - NS_WARNING("Using integer IDs without compat mode enabled"); - - RefPtr dl = FindDownload(aID); - MOZ_ASSERT(!dl, "Can't call RemoveDownload on a download in progress!"); - if (dl) - return NS_ERROR_FAILURE; - - nsresult rv = GetDownloadFromDB(aID, getter_AddRefs(dl)); - NS_ENSURE_SUCCESS(rv, rv); - - nsCOMPtr stmt; - rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING( - "DELETE FROM moz_downloads " - "WHERE id = :id"), getter_AddRefs(stmt)); - NS_ENSURE_SUCCESS(rv, rv); - - rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("id"), aID); // unsigned; 64-bit to prevent overflow - NS_ENSURE_SUCCESS(rv, rv); - - rv = stmt->Execute(); - NS_ENSURE_SUCCESS(rv, rv); - - // Notify the UI with the topic and download id - return NotifyDownloadRemoval(dl); -} - -nsresult -nsDownloadManager::NotifyDownloadRemoval(nsDownload* aRemoved) -{ - nsCOMPtr id; - nsCOMPtr guid; - nsresult rv; - - // Only send an integer ID notification if the download is public. - bool sendDeprecatedNotification = !(aRemoved && aRemoved->mPrivate); - - if (sendDeprecatedNotification && aRemoved) { - id = do_CreateInstance(NS_SUPPORTS_PRUINT32_CONTRACTID, &rv); - NS_ENSURE_SUCCESS(rv, rv); - uint32_t dlID; - rv = aRemoved->GetId(&dlID); - NS_ENSURE_SUCCESS(rv, rv); - rv = id->SetData(dlID); - NS_ENSURE_SUCCESS(rv, rv); - } - - if (sendDeprecatedNotification) { - mObserverService->NotifyObservers(id, - "download-manager-remove-download", - nullptr); - } - - if (aRemoved) { - guid = do_CreateInstance(NS_SUPPORTS_CSTRING_CONTRACTID, &rv); - NS_ENSURE_SUCCESS(rv, rv); - nsAutoCString guidStr; - rv = aRemoved->GetGuid(guidStr); - NS_ENSURE_SUCCESS(rv, rv); - rv = guid->SetData(guidStr); - NS_ENSURE_SUCCESS(rv, rv); - } - - mObserverService->NotifyObservers(guid, - "download-manager-remove-download-guid", - nullptr); - return NS_OK; -} - -static nsresult -DoRemoveDownloadsByTimeframe(mozIStorageConnection* aDBConn, - int64_t aStartTime, - int64_t aEndTime) -{ - nsCOMPtr stmt; - nsresult rv = aDBConn->CreateStatement(NS_LITERAL_CSTRING( - "DELETE FROM moz_downloads " - "WHERE startTime >= :startTime " - "AND startTime <= :endTime " - "AND state NOT IN (:downloading, :paused, :queued)"), getter_AddRefs(stmt)); - NS_ENSURE_SUCCESS(rv, rv); - - // Bind the times - rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("startTime"), aStartTime); - NS_ENSURE_SUCCESS(rv, rv); - rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("endTime"), aEndTime); - NS_ENSURE_SUCCESS(rv, rv); - - // Bind the active states - rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("downloading"), nsIDownloadManager::DOWNLOAD_DOWNLOADING); - NS_ENSURE_SUCCESS(rv, rv); - rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("paused"), nsIDownloadManager::DOWNLOAD_PAUSED); - NS_ENSURE_SUCCESS(rv, rv); - rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("queued"), nsIDownloadManager::DOWNLOAD_QUEUED); - NS_ENSURE_SUCCESS(rv, rv); - - // Execute - rv = stmt->Execute(); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; -} - -NS_IMETHODIMP -nsDownloadManager::RemoveDownloadsByTimeframe(int64_t aStartTime, - int64_t aEndTime) -{ - NS_ENSURE_STATE(!mUseJSTransfer); - - nsresult rv = DoRemoveDownloadsByTimeframe(mDBConn, aStartTime, aEndTime); - nsresult rv2 = DoRemoveDownloadsByTimeframe(mPrivateDBConn, aStartTime, aEndTime); - NS_ENSURE_SUCCESS(rv, rv); - NS_ENSURE_SUCCESS(rv2, rv2); - - // Notify the UI with the topic and null subject to indicate "remove multiple" - return NotifyDownloadRemoval(nullptr); -} - -NS_IMETHODIMP -nsDownloadManager::CleanUp() -{ - NS_ENSURE_STATE(!mUseJSTransfer); - - return CleanUp(mDBConn); -} - -NS_IMETHODIMP -nsDownloadManager::CleanUpPrivate() -{ - NS_ENSURE_STATE(!mUseJSTransfer); - - return CleanUp(mPrivateDBConn); -} - -nsresult -nsDownloadManager::CleanUp(mozIStorageConnection* aDBConn) -{ - DownloadState states[] = { nsIDownloadManager::DOWNLOAD_FINISHED, - nsIDownloadManager::DOWNLOAD_FAILED, - nsIDownloadManager::DOWNLOAD_CANCELED, - nsIDownloadManager::DOWNLOAD_BLOCKED_PARENTAL, - nsIDownloadManager::DOWNLOAD_BLOCKED_POLICY, - nsIDownloadManager::DOWNLOAD_DIRTY }; - - nsCOMPtr stmt; - nsresult rv = aDBConn->CreateStatement(NS_LITERAL_CSTRING( - "DELETE FROM moz_downloads " - "WHERE state = ? " - "OR state = ? " - "OR state = ? " - "OR state = ? " - "OR state = ? " - "OR state = ?"), getter_AddRefs(stmt)); - NS_ENSURE_SUCCESS(rv, rv); - for (uint32_t i = 0; i < ArrayLength(states); ++i) { - rv = stmt->BindInt32ByIndex(i, states[i]); - NS_ENSURE_SUCCESS(rv, rv); - } - - rv = stmt->Execute(); - NS_ENSURE_SUCCESS(rv, rv); - - // Notify the UI with the topic and null subject to indicate "remove multiple" - return NotifyDownloadRemoval(nullptr); -} - -static nsresult -DoGetCanCleanUp(mozIStorageConnection* aDBConn, bool *aResult) -{ - // This method should never return anything but NS_OK for the benefit of - // unwitting consumers. - - *aResult = false; - - DownloadState states[] = { nsIDownloadManager::DOWNLOAD_FINISHED, - nsIDownloadManager::DOWNLOAD_FAILED, - nsIDownloadManager::DOWNLOAD_CANCELED, - nsIDownloadManager::DOWNLOAD_BLOCKED_PARENTAL, - nsIDownloadManager::DOWNLOAD_BLOCKED_POLICY, - nsIDownloadManager::DOWNLOAD_DIRTY }; - - nsCOMPtr stmt; - nsresult rv = aDBConn->CreateStatement(NS_LITERAL_CSTRING( - "SELECT COUNT(*) " - "FROM moz_downloads " - "WHERE state = ? " - "OR state = ? " - "OR state = ? " - "OR state = ? " - "OR state = ? " - "OR state = ?"), getter_AddRefs(stmt)); - NS_ENSURE_SUCCESS(rv, NS_OK); - for (uint32_t i = 0; i < ArrayLength(states); ++i) { - rv = stmt->BindInt32ByIndex(i, states[i]); - NS_ENSURE_SUCCESS(rv, NS_OK); - } - - bool moreResults; // We don't really care... - rv = stmt->ExecuteStep(&moreResults); - NS_ENSURE_SUCCESS(rv, NS_OK); - - int32_t count; - rv = stmt->GetInt32(0, &count); - NS_ENSURE_SUCCESS(rv, NS_OK); - - if (count > 0) - *aResult = true; - - return NS_OK; -} - -NS_IMETHODIMP -nsDownloadManager::GetCanCleanUp(bool *aResult) -{ - NS_ENSURE_STATE(!mUseJSTransfer); - - return DoGetCanCleanUp(mDBConn, aResult); -} - -NS_IMETHODIMP -nsDownloadManager::GetCanCleanUpPrivate(bool *aResult) -{ - NS_ENSURE_STATE(!mUseJSTransfer); - - return DoGetCanCleanUp(mPrivateDBConn, aResult); -} - -NS_IMETHODIMP -nsDownloadManager::PauseDownload(uint32_t aID) -{ - NS_ENSURE_STATE(!mUseJSTransfer); - - NS_WARNING("Using integer IDs without compat mode enabled"); - - nsDownload *dl = FindDownload(aID); - if (!dl) - return NS_ERROR_FAILURE; - - return dl->Pause(); -} - -NS_IMETHODIMP -nsDownloadManager::ResumeDownload(uint32_t aID) -{ - NS_ENSURE_STATE(!mUseJSTransfer); - - NS_WARNING("Using integer IDs without compat mode enabled"); - - nsDownload *dl = FindDownload(aID); - if (!dl) - return NS_ERROR_FAILURE; - - return dl->Resume(); -} - -NS_IMETHODIMP -nsDownloadManager::GetDBConnection(mozIStorageConnection **aDBConn) -{ - NS_ENSURE_STATE(!mUseJSTransfer); - - NS_ADDREF(*aDBConn = mDBConn); - - return NS_OK; -} - -NS_IMETHODIMP -nsDownloadManager::GetPrivateDBConnection(mozIStorageConnection **aDBConn) -{ - NS_ENSURE_STATE(!mUseJSTransfer); - - NS_ADDREF(*aDBConn = mPrivateDBConn); - - return NS_OK; - } - -NS_IMETHODIMP -nsDownloadManager::AddListener(nsIDownloadProgressListener *aListener) -{ - NS_ENSURE_STATE(!mUseJSTransfer); - - mListeners.AppendObject(aListener); - return NS_OK; -} - -NS_IMETHODIMP -nsDownloadManager::AddPrivacyAwareListener(nsIDownloadProgressListener *aListener) -{ - NS_ENSURE_STATE(!mUseJSTransfer); - - mPrivacyAwareListeners.AppendObject(aListener); - return NS_OK; -} - -NS_IMETHODIMP -nsDownloadManager::RemoveListener(nsIDownloadProgressListener *aListener) -{ - NS_ENSURE_STATE(!mUseJSTransfer); - - mListeners.RemoveObject(aListener); - mPrivacyAwareListeners.RemoveObject(aListener); - return NS_OK; -} - -void -nsDownloadManager::NotifyListenersOnDownloadStateChange(int16_t aOldState, - nsDownload *aDownload) -{ - for (int32_t i = mPrivacyAwareListeners.Count() - 1; i >= 0; --i) { - mPrivacyAwareListeners[i]->OnDownloadStateChange(aOldState, aDownload); - } - - // Only privacy-aware listeners should receive notifications about private - // downloads, while non-privacy-aware listeners receive no sign they exist. - if (aDownload->mPrivate) { - return; - } - - for (int32_t i = mListeners.Count() - 1; i >= 0; --i) { - mListeners[i]->OnDownloadStateChange(aOldState, aDownload); - } -} - -void -nsDownloadManager::NotifyListenersOnProgressChange(nsIWebProgress *aProgress, - nsIRequest *aRequest, - int64_t aCurSelfProgress, - int64_t aMaxSelfProgress, - int64_t aCurTotalProgress, - int64_t aMaxTotalProgress, - nsDownload *aDownload) -{ - for (int32_t i = mPrivacyAwareListeners.Count() - 1; i >= 0; --i) { - mPrivacyAwareListeners[i]->OnProgressChange(aProgress, aRequest, aCurSelfProgress, - aMaxSelfProgress, aCurTotalProgress, - aMaxTotalProgress, aDownload); - } - - // Only privacy-aware listeners should receive notifications about private - // downloads, while non-privacy-aware listeners receive no sign they exist. - if (aDownload->mPrivate) { - return; - } - - for (int32_t i = mListeners.Count() - 1; i >= 0; --i) { - mListeners[i]->OnProgressChange(aProgress, aRequest, aCurSelfProgress, - aMaxSelfProgress, aCurTotalProgress, - aMaxTotalProgress, aDownload); - } -} - -void -nsDownloadManager::NotifyListenersOnStateChange(nsIWebProgress *aProgress, - nsIRequest *aRequest, - uint32_t aStateFlags, - nsresult aStatus, - nsDownload *aDownload) -{ - for (int32_t i = mPrivacyAwareListeners.Count() - 1; i >= 0; --i) { - mPrivacyAwareListeners[i]->OnStateChange(aProgress, aRequest, aStateFlags, aStatus, - aDownload); - } - - // Only privacy-aware listeners should receive notifications about private - // downloads, while non-privacy-aware listeners receive no sign they exist. - if (aDownload->mPrivate) { - return; - } - - for (int32_t i = mListeners.Count() - 1; i >= 0; --i) { - mListeners[i]->OnStateChange(aProgress, aRequest, aStateFlags, aStatus, - aDownload); - } -} - -//////////////////////////////////////////////////////////////////////////////// -//// nsINavHistoryObserver - -NS_IMETHODIMP -nsDownloadManager::OnBeginUpdateBatch() -{ - // This method in not normally invoked when mUseJSTransfer is enabled, however - // we provide an extra check in case it is called manually by add-ons. - NS_ENSURE_STATE(!mUseJSTransfer); - - // We already have a transaction, so don't make another - if (mHistoryTransaction) - return NS_OK; - - // Start a transaction that commits when deleted - mHistoryTransaction = new mozStorageTransaction(mDBConn, true); - - return NS_OK; -} - -NS_IMETHODIMP -nsDownloadManager::OnEndUpdateBatch() -{ - // Get rid of the transaction and cause it to commit - mHistoryTransaction = nullptr; - - return NS_OK; -} - -NS_IMETHODIMP -nsDownloadManager::OnVisit(nsIURI *aURI, int64_t aVisitID, PRTime aTime, - int64_t aSessionID, int64_t aReferringID, - uint32_t aTransitionType, const nsACString& aGUID, - bool aHidden, uint32_t aVisitCount, uint32_t aTyped, - const nsAString& aLastKnowntTitle) -{ - return NS_OK; -} - -NS_IMETHODIMP -nsDownloadManager::OnTitleChanged(nsIURI *aURI, - const nsAString &aPageTitle, - const nsACString &aGUID) -{ - return NS_OK; -} - -NS_IMETHODIMP -nsDownloadManager::OnFrecencyChanged(nsIURI* aURI, - int32_t aNewFrecency, - const nsACString& aGUID, - bool aHidden, - PRTime aLastVisitDate) -{ - return NS_OK; -} - -NS_IMETHODIMP -nsDownloadManager::OnManyFrecenciesChanged() -{ - return NS_OK; -} - -NS_IMETHODIMP -nsDownloadManager::OnDeleteURI(nsIURI *aURI, - const nsACString& aGUID, - uint16_t aReason) -{ - // This method in not normally invoked when mUseJSTransfer is enabled, however - // we provide an extra check in case it is called manually by add-ons. - NS_ENSURE_STATE(!mUseJSTransfer); - - nsresult rv = RemoveDownloadsForURI(mGetIdsForURIStatement, aURI); - nsresult rv2 = RemoveDownloadsForURI(mGetPrivateIdsForURIStatement, aURI); - NS_ENSURE_SUCCESS(rv, rv); - NS_ENSURE_SUCCESS(rv2, rv2); - return NS_OK; -} - -NS_IMETHODIMP -nsDownloadManager::OnClearHistory() -{ - return CleanUp(); -} - -NS_IMETHODIMP -nsDownloadManager::OnPageChanged(nsIURI *aURI, - uint32_t aChangedAttribute, - const nsAString& aNewValue, - const nsACString &aGUID) -{ - return NS_OK; -} - -NS_IMETHODIMP -nsDownloadManager::OnDeleteVisits(nsIURI *aURI, PRTime aVisitTime, - const nsACString& aGUID, - uint16_t aReason, uint32_t aTransitionType) -{ - // Don't bother removing downloads until the page is removed. - return NS_OK; -} - -//////////////////////////////////////////////////////////////////////////////// -//// nsIObserver - -NS_IMETHODIMP -nsDownloadManager::Observe(nsISupports *aSubject, - const char *aTopic, - const char16_t *aData) -{ - // This method in not normally invoked when mUseJSTransfer is enabled, however - // we provide an extra check in case it is called manually by add-ons. - NS_ENSURE_STATE(!mUseJSTransfer); - - // We need to count the active public downloads that could be lost - // by quitting, and add any active private ones as well, since per-window - // private browsing may be active. - int32_t currDownloadCount = mCurrentDownloads.Count(); - - // If we don't need to cancel all the downloads on quit, only count the ones - // that aren't resumable. - if (GetQuitBehavior() != QUIT_AND_CANCEL) { - for (int32_t i = currDownloadCount - 1; i >= 0; --i) { - if (mCurrentDownloads[i]->IsResumable()) { - currDownloadCount--; - } - } - - // We have a count of the public, non-resumable downloads. Now we need - // to add the total number of private downloads, since they are in danger - // of being lost. - currDownloadCount += mCurrentPrivateDownloads.Count(); - } - - nsresult rv; - if (strcmp(aTopic, "oncancel") == 0) { - nsCOMPtr dl = do_QueryInterface(aSubject, &rv); - NS_ENSURE_SUCCESS(rv, rv); - - dl->Cancel(); - } else if (strcmp(aTopic, "profile-before-change") == 0) { - CloseAllDBs(); - } else if (strcmp(aTopic, "quit-application") == 0) { - // Try to pause all downloads and, if appropriate, mark them as auto-resume - // unless user has specified that downloads should be canceled - enum QuitBehavior behavior = GetQuitBehavior(); - if (behavior != QUIT_AND_CANCEL) - (void)PauseAllDownloads(bool(behavior != QUIT_AND_PAUSE)); - - // Remove downloads to break cycles and cancel downloads - (void)RemoveAllDownloads(); - - // Now that active downloads have been canceled, remove all completed or - // aborted downloads if the user's retention policy specifies it. - if (GetRetentionBehavior() == 1) - CleanUp(); - } else if (strcmp(aTopic, "quit-application-requested") == 0 && - currDownloadCount) { - nsCOMPtr cancelDownloads = - do_QueryInterface(aSubject, &rv); - NS_ENSURE_SUCCESS(rv, rv); -#ifndef XP_MACOSX - ConfirmCancelDownloads(currDownloadCount, cancelDownloads, - u"quitCancelDownloadsAlertTitle", - u"quitCancelDownloadsAlertMsgMultiple", - u"quitCancelDownloadsAlertMsg", - u"dontQuitButtonWin"); -#else - ConfirmCancelDownloads(currDownloadCount, cancelDownloads, - u"quitCancelDownloadsAlertTitle", - u"quitCancelDownloadsAlertMsgMacMultiple", - u"quitCancelDownloadsAlertMsgMac", - u"dontQuitButtonMac"); -#endif - } else if (strcmp(aTopic, "offline-requested") == 0 && currDownloadCount) { - nsCOMPtr cancelDownloads = - do_QueryInterface(aSubject, &rv); - NS_ENSURE_SUCCESS(rv, rv); - ConfirmCancelDownloads(currDownloadCount, cancelDownloads, - u"offlineCancelDownloadsAlertTitle", - u"offlineCancelDownloadsAlertMsgMultiple", - u"offlineCancelDownloadsAlertMsg", - u"dontGoOfflineButton"); - } - else if (strcmp(aTopic, NS_IOSERVICE_GOING_OFFLINE_TOPIC) == 0) { - // Pause all downloads, and mark them to auto-resume. - (void)PauseAllDownloads(true); - } - else if (strcmp(aTopic, NS_IOSERVICE_OFFLINE_STATUS_TOPIC) == 0 && - nsDependentString(aData).EqualsLiteral(NS_IOSERVICE_ONLINE)) { - // We can now resume all downloads that are supposed to auto-resume. - (void)ResumeAllDownloads(false); - } - else if (strcmp(aTopic, "alertclickcallback") == 0) { - nsCOMPtr dmui = - do_GetService("@mozilla.org/download-manager-ui;1", &rv); - NS_ENSURE_SUCCESS(rv, rv); - return dmui->Show(nullptr, nullptr, nsIDownloadManagerUI::REASON_USER_INTERACTED, - aData && NS_strcmp(aData, u"private") == 0); - } else if (strcmp(aTopic, "sleep_notification") == 0 || - strcmp(aTopic, "suspend_process_notification") == 0) { - // Pause downloads if we're sleeping, and mark the downloads as auto-resume - (void)PauseAllDownloads(true); - } else if (strcmp(aTopic, "wake_notification") == 0 || - strcmp(aTopic, "resume_process_notification") == 0) { - int32_t resumeOnWakeDelay = 10000; - nsCOMPtr pref = do_GetService(NS_PREFSERVICE_CONTRACTID); - if (pref) - (void)pref->GetIntPref(PREF_BDM_RESUMEONWAKEDELAY, &resumeOnWakeDelay); - - // Wait a little bit before trying to resume to avoid resuming when network - // connections haven't restarted yet - mResumeOnWakeTimer = do_CreateInstance("@mozilla.org/timer;1"); - if (resumeOnWakeDelay >= 0 && mResumeOnWakeTimer) { - (void)mResumeOnWakeTimer->InitWithFuncCallback(ResumeOnWakeCallback, - this, resumeOnWakeDelay, nsITimer::TYPE_ONE_SHOT); - } - } else if (strcmp(aTopic, "last-pb-context-exited") == 0) { - // Upon leaving private browsing mode, cancel all private downloads, - // remove all trace of them, and then blow away the private database - // and recreate a blank one. - RemoveAllDownloads(mCurrentPrivateDownloads); - InitPrivateDB(); - } else if (strcmp(aTopic, "last-pb-context-exiting") == 0) { - // If there are active private downloads, prompt the user to confirm leaving - // private browsing mode (thereby cancelling them). Otherwise, silently proceed. - if (!mCurrentPrivateDownloads.Count()) - return NS_OK; - - nsCOMPtr cancelDownloads = do_QueryInterface(aSubject, &rv); - NS_ENSURE_SUCCESS(rv, rv); - - ConfirmCancelDownloads(mCurrentPrivateDownloads.Count(), cancelDownloads, - u"leavePrivateBrowsingCancelDownloadsAlertTitle", - u"leavePrivateBrowsingWindowsCancelDownloadsAlertMsgMultiple2", - u"leavePrivateBrowsingWindowsCancelDownloadsAlertMsg2", - u"dontLeavePrivateBrowsingButton2"); - } - - return NS_OK; -} - -void -nsDownloadManager::ConfirmCancelDownloads(int32_t aCount, - nsISupportsPRBool *aCancelDownloads, - const char16_t *aTitle, - const char16_t *aCancelMessageMultiple, - const char16_t *aCancelMessageSingle, - const char16_t *aDontCancelButton) -{ - // If user has already dismissed quit request, then do nothing - bool quitRequestCancelled = false; - aCancelDownloads->GetData(&quitRequestCancelled); - if (quitRequestCancelled) - return; - - nsXPIDLString title, message, quitButton, dontQuitButton; - - mBundle->GetStringFromName(aTitle, getter_Copies(title)); - - nsAutoString countString; - countString.AppendInt(aCount); - const char16_t *strings[1] = { countString.get() }; - if (aCount > 1) { - mBundle->FormatStringFromName(aCancelMessageMultiple, strings, 1, - getter_Copies(message)); - mBundle->FormatStringFromName(u"cancelDownloadsOKTextMultiple", - strings, 1, getter_Copies(quitButton)); - } else { - mBundle->GetStringFromName(aCancelMessageSingle, getter_Copies(message)); - mBundle->GetStringFromName(u"cancelDownloadsOKText", - getter_Copies(quitButton)); - } - - mBundle->GetStringFromName(aDontCancelButton, getter_Copies(dontQuitButton)); - - // Get Download Manager window, to be parent of alert. - nsCOMPtr wm = do_GetService(NS_WINDOWMEDIATOR_CONTRACTID); - nsCOMPtr dmWindow; - if (wm) { - wm->GetMostRecentWindow(u"Download:Manager", - getter_AddRefs(dmWindow)); - } - - // Show alert. - nsCOMPtr prompter(do_GetService(NS_PROMPTSERVICE_CONTRACTID)); - if (prompter) { - int32_t flags = (nsIPromptService::BUTTON_TITLE_IS_STRING * nsIPromptService::BUTTON_POS_0) + (nsIPromptService::BUTTON_TITLE_IS_STRING * nsIPromptService::BUTTON_POS_1); - bool nothing = false; - int32_t button; - prompter->ConfirmEx(dmWindow, title, message, flags, quitButton.get(), dontQuitButton.get(), nullptr, nullptr, ¬hing, &button); - - aCancelDownloads->SetData(button == 1); - } -} - -//////////////////////////////////////////////////////////////////////////////// -//// nsDownload - -NS_IMPL_CLASSINFO(nsDownload, nullptr, 0, NS_DOWNLOAD_CID) -NS_IMPL_ISUPPORTS_CI( - nsDownload - , nsIDownload - , nsITransfer - , nsIWebProgressListener - , nsIWebProgressListener2 -) - -nsDownload::nsDownload() : mDownloadState(nsIDownloadManager::DOWNLOAD_NOTSTARTED), - mID(0), - mPercentComplete(0), - mCurrBytes(0), - mMaxBytes(-1), - mStartTime(0), - mLastUpdate(PR_Now() - (uint32_t)gUpdateInterval), - mResumedAt(-1), - mSpeed(0), - mHasMultipleFiles(false), - mPrivate(false), - mAutoResume(DONT_RESUME) -{ -} - -nsDownload::~nsDownload() -{ -} - -NS_IMETHODIMP nsDownload::SetSha256Hash(const nsACString& aHash) { - MOZ_ASSERT(NS_IsMainThread(), "Must call SetSha256Hash on main thread"); - // This will be used later to query the application reputation service. - mHash = aHash; - return NS_OK; -} - -NS_IMETHODIMP nsDownload::SetSignatureInfo(nsIArray* aSignatureInfo) { - MOZ_ASSERT(NS_IsMainThread(), "Must call SetSignatureInfo on main thread"); - // This will be used later to query the application reputation service. - mSignatureInfo = aSignatureInfo; - return NS_OK; -} - -NS_IMETHODIMP nsDownload::SetRedirects(nsIArray* aRedirects) { - MOZ_ASSERT(NS_IsMainThread(), "Must call SetRedirects on main thread"); - // This will be used later to query the application reputation service. - mRedirects = aRedirects; - return NS_OK; -} - -#ifdef MOZ_WIDGET_GTK -static void gio_set_metadata_done(GObject *source_obj, GAsyncResult *res, gpointer user_data) -{ - GError *err = nullptr; - g_file_set_attributes_finish(G_FILE(source_obj), res, nullptr, &err); - if (err) { -#ifdef DEBUG - NS_DebugBreak(NS_DEBUG_WARNING, "Set file metadata failed: ", err->message, __FILE__, __LINE__); -#endif - g_error_free(err); - } -} -#endif - -nsresult -nsDownload::SetState(DownloadState aState) -{ - NS_ASSERTION(mDownloadState != aState, - "Trying to set the download state to what it already is set to!"); - - int16_t oldState = mDownloadState; - mDownloadState = aState; - - // We don't want to lose access to our member variables - RefPtr kungFuDeathGrip = this; - - // When the state changed listener is dispatched, queries to the database and - // the download manager api should reflect what the nsIDownload object would - // return. So, if a download is done (finished, canceled, etc.), it should - // first be removed from the current downloads. We will also have to update - // the database *before* notifying listeners. At this point, you can safely - // dispatch to the observers as well. - switch (aState) { - case nsIDownloadManager::DOWNLOAD_BLOCKED_PARENTAL: - case nsIDownloadManager::DOWNLOAD_BLOCKED_POLICY: - case nsIDownloadManager::DOWNLOAD_DIRTY: - case nsIDownloadManager::DOWNLOAD_CANCELED: - case nsIDownloadManager::DOWNLOAD_FAILED: -#ifdef ANDROID - // If we still have a temp file, remove it - bool tempExists; - if (mTempFile && NS_SUCCEEDED(mTempFile->Exists(&tempExists)) && tempExists) { - nsresult rv = mTempFile->Remove(false); - NS_ENSURE_SUCCESS(rv, rv); - } -#endif - - // Transfers are finished, so break the reference cycle - Finalize(); - break; -#ifdef DOWNLOAD_SCANNER - case nsIDownloadManager::DOWNLOAD_SCANNING: - { - nsresult rv = mDownloadManager->mScanner ? mDownloadManager->mScanner->ScanDownload(this) : NS_ERROR_NOT_INITIALIZED; - // If we failed, then fall through to 'download finished' - if (NS_SUCCEEDED(rv)) - break; - mDownloadState = aState = nsIDownloadManager::DOWNLOAD_FINISHED; - } -#endif - case nsIDownloadManager::DOWNLOAD_FINISHED: - { - nsresult rv = ExecuteDesiredAction(); - if (NS_FAILED(rv)) { - // We've failed to execute the desired action. As a result, we should - // fail the download so the user can try again. - (void)FailDownload(rv, nullptr); - return rv; - } - - // Now that we're done with handling the download, clean it up - Finalize(); - - nsCOMPtr pref(do_GetService(NS_PREFSERVICE_CONTRACTID)); - - // Master pref to control this function. - bool showTaskbarAlert = true; - if (pref) - pref->GetBoolPref(PREF_BDM_SHOWALERTONCOMPLETE, &showTaskbarAlert); - - if (showTaskbarAlert) { - int32_t alertInterval = 2000; - if (pref) - pref->GetIntPref(PREF_BDM_SHOWALERTINTERVAL, &alertInterval); - - int64_t alertIntervalUSec = alertInterval * PR_USEC_PER_MSEC; - int64_t goat = PR_Now() - mStartTime; - showTaskbarAlert = goat > alertIntervalUSec; - - int32_t size = mPrivate ? - mDownloadManager->mCurrentPrivateDownloads.Count() : - mDownloadManager->mCurrentDownloads.Count(); - if (showTaskbarAlert && size == 0) { - nsCOMPtr alerts = - do_GetService("@mozilla.org/alerts-service;1"); - if (alerts) { - nsXPIDLString title, message; - - mDownloadManager->mBundle->GetStringFromName( - u"downloadsCompleteTitle", - getter_Copies(title)); - mDownloadManager->mBundle->GetStringFromName( - u"downloadsCompleteMsg", - getter_Copies(message)); - - bool removeWhenDone = - mDownloadManager->GetRetentionBehavior() == 0; - - // If downloads are automatically removed per the user's - // retention policy, there's no reason to make the text clickable - // because if it is, they'll click open the download manager and - // the items they downloaded will have been removed. - alerts->ShowAlertNotification( - NS_LITERAL_STRING(DOWNLOAD_MANAGER_ALERT_ICON), title, - message, !removeWhenDone, - mPrivate ? NS_LITERAL_STRING("private") : NS_LITERAL_STRING("non-private"), - mDownloadManager, EmptyString(), NS_LITERAL_STRING("auto"), - EmptyString(), EmptyString(), nullptr, mPrivate, - false /* requireInteraction */); - } - } - } - -#if defined(XP_WIN) || defined(XP_MACOSX) || defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GTK) - nsCOMPtr fileURL = do_QueryInterface(mTarget); - nsCOMPtr file; - nsAutoString path; - - if (fileURL && - NS_SUCCEEDED(fileURL->GetFile(getter_AddRefs(file))) && - file && - NS_SUCCEEDED(file->GetPath(path))) { - -#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK) || defined(MOZ_WIDGET_ANDROID) - // On Windows and Gtk, add the download to the system's "recent documents" - // list, with a pref to disable. - { - bool addToRecentDocs = true; - if (pref) - pref->GetBoolPref(PREF_BDM_ADDTORECENTDOCS, &addToRecentDocs); -#ifdef MOZ_WIDGET_ANDROID - if (addToRecentDocs) { - nsCOMPtr mimeInfo; - nsAutoCString contentType; - GetMIMEInfo(getter_AddRefs(mimeInfo)); - - if (mimeInfo) - mimeInfo->GetMIMEType(contentType); - - if (jni::IsFennec()) { - java::DownloadsIntegration::ScanMedia(path, NS_ConvertUTF8toUTF16(contentType)); - } - } -#else - if (addToRecentDocs && !mPrivate) { -#ifdef XP_WIN - ::SHAddToRecentDocs(SHARD_PATHW, path.get()); -#elif defined(MOZ_WIDGET_GTK) - GtkRecentManager* manager = gtk_recent_manager_get_default(); - - gchar* uri = g_filename_to_uri(NS_ConvertUTF16toUTF8(path).get(), - nullptr, nullptr); - if (uri) { - gtk_recent_manager_add_item(manager, uri); - g_free(uri); - } -#endif - } -#endif -#ifdef MOZ_WIDGET_GTK - // Use GIO to store the source URI for later display in the file manager. - GFile* gio_file = g_file_new_for_path(NS_ConvertUTF16toUTF8(path).get()); - nsCString source_uri; - rv = mSource->GetSpec(source_uri); - NS_ENSURE_SUCCESS(rv, rv); - GFileInfo *file_info = g_file_info_new(); - g_file_info_set_attribute_string(file_info, "metadata::download-uri", source_uri.get()); - g_file_set_attributes_async(gio_file, - file_info, - G_FILE_QUERY_INFO_NONE, - G_PRIORITY_DEFAULT, - nullptr, gio_set_metadata_done, nullptr); - g_object_unref(file_info); - g_object_unref(gio_file); -#endif - } -#endif - -#ifdef XP_MACOSX - // On OS X, make the downloads stack bounce. - CFStringRef observedObject = ::CFStringCreateWithCString(kCFAllocatorDefault, - NS_ConvertUTF16toUTF8(path).get(), - kCFStringEncodingUTF8); - CFNotificationCenterRef center = ::CFNotificationCenterGetDistributedCenter(); - ::CFNotificationCenterPostNotification(center, CFSTR("com.apple.DownloadFileFinished"), - observedObject, nullptr, TRUE); - ::CFRelease(observedObject); -#endif - } - -#ifdef XP_WIN - // Adjust file attributes so that by default, new files are indexed - // by desktop search services. Skip off those that land in the temp - // folder. - nsCOMPtr tempDir, fileDir; - rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(tempDir)); - NS_ENSURE_SUCCESS(rv, rv); - (void)file->GetParent(getter_AddRefs(fileDir)); - - bool isTemp = false; - if (fileDir) - (void)fileDir->Equals(tempDir, &isTemp); - - nsCOMPtr localFileWin(do_QueryInterface(file)); - if (!isTemp && localFileWin) - (void)localFileWin->SetFileAttributesWin(nsILocalFileWin::WFA_SEARCH_INDEXED); -#endif - -#endif - // Now remove the download if the user's retention policy is "Remove when Done" - if (mDownloadManager->GetRetentionBehavior() == 0) - mDownloadManager->RemoveDownload(mGUID); - } - break; - default: - break; - } - - // Before notifying the listener, we must update the database so that calls - // to it work out properly. - nsresult rv = UpdateDB(); - NS_ENSURE_SUCCESS(rv, rv); - - mDownloadManager->NotifyListenersOnDownloadStateChange(oldState, this); - - switch (mDownloadState) { - case nsIDownloadManager::DOWNLOAD_DOWNLOADING: - // Only send the dl-start event to downloads that are actually starting. - if (oldState == nsIDownloadManager::DOWNLOAD_QUEUED) { - if (!mPrivate) - mDownloadManager->SendEvent(this, "dl-start"); - } - break; - case nsIDownloadManager::DOWNLOAD_FAILED: - if (!mPrivate) - mDownloadManager->SendEvent(this, "dl-failed"); - break; - case nsIDownloadManager::DOWNLOAD_SCANNING: - if (!mPrivate) - mDownloadManager->SendEvent(this, "dl-scanning"); - break; - case nsIDownloadManager::DOWNLOAD_FINISHED: - if (!mPrivate) - mDownloadManager->SendEvent(this, "dl-done"); - break; - case nsIDownloadManager::DOWNLOAD_BLOCKED_PARENTAL: - case nsIDownloadManager::DOWNLOAD_BLOCKED_POLICY: - if (!mPrivate) - mDownloadManager->SendEvent(this, "dl-blocked"); - break; - case nsIDownloadManager::DOWNLOAD_DIRTY: - if (!mPrivate) - mDownloadManager->SendEvent(this, "dl-dirty"); - break; - case nsIDownloadManager::DOWNLOAD_CANCELED: - if (!mPrivate) - mDownloadManager->SendEvent(this, "dl-cancel"); - break; - default: - break; - } - return NS_OK; -} - -//////////////////////////////////////////////////////////////////////////////// -//// nsIWebProgressListener2 - -NS_IMETHODIMP -nsDownload::OnProgressChange64(nsIWebProgress *aWebProgress, - nsIRequest *aRequest, - int64_t aCurSelfProgress, - int64_t aMaxSelfProgress, - int64_t aCurTotalProgress, - int64_t aMaxTotalProgress) -{ - if (!mRequest) - mRequest = aRequest; // used for pause/resume - - if (mDownloadState == nsIDownloadManager::DOWNLOAD_QUEUED) { - // Obtain the referrer - nsresult rv; - nsCOMPtr channel(do_QueryInterface(aRequest)); - nsCOMPtr referrer = mReferrer; - if (channel) - (void)NS_GetReferrerFromChannel(channel, getter_AddRefs(mReferrer)); - - // Restore the original referrer if the new one isn't useful - if (!mReferrer) - mReferrer = referrer; - - // If we have a MIME info, we know that exthandler has already added this to - // the history, but if we do not, we'll have to add it ourselves. - if (!mMIMEInfo && !mPrivate) { - nsCOMPtr dh = - do_GetService(NS_DOWNLOADHISTORY_CONTRACTID); - if (dh) - (void)dh->AddDownload(mSource, mReferrer, mStartTime, mTarget); - } - - // Fetch the entityID, but if we can't get it, don't panic (non-resumable) - nsCOMPtr resumableChannel(do_QueryInterface(aRequest)); - if (resumableChannel) - (void)resumableChannel->GetEntityID(mEntityID); - - // Before we update the state and dispatch state notifications, we want to - // ensure that we have the correct state for this download with regards to - // its percent completion and size. - SetProgressBytes(0, aMaxTotalProgress); - - // Update the state and the database - rv = SetState(nsIDownloadManager::DOWNLOAD_DOWNLOADING); - NS_ENSURE_SUCCESS(rv, rv); - } - - // filter notifications since they come in so frequently - PRTime now = PR_Now(); - PRIntervalTime delta = now - mLastUpdate; - if (delta < gUpdateInterval) - return NS_OK; - - mLastUpdate = now; - - // Calculate the speed using the elapsed delta time and bytes downloaded - // during that time for more accuracy. - double elapsedSecs = double(delta) / PR_USEC_PER_SEC; - if (elapsedSecs > 0) { - double speed = double(aCurTotalProgress - mCurrBytes) / elapsedSecs; - if (mCurrBytes == 0) { - mSpeed = speed; - } else { - // Calculate 'smoothed average' of 10 readings. - mSpeed = mSpeed * 0.9 + speed * 0.1; - } - } - - SetProgressBytes(aCurTotalProgress, aMaxTotalProgress); - - // Report to the listener our real sizes - int64_t currBytes, maxBytes; - (void)GetAmountTransferred(&currBytes); - (void)GetSize(&maxBytes); - mDownloadManager->NotifyListenersOnProgressChange( - aWebProgress, aRequest, currBytes, maxBytes, currBytes, maxBytes, this); - - // If the maximums are different, then there must be more than one file - if (aMaxSelfProgress != aMaxTotalProgress) - mHasMultipleFiles = true; - - return NS_OK; -} - -NS_IMETHODIMP -nsDownload::OnRefreshAttempted(nsIWebProgress *aWebProgress, - nsIURI *aUri, - int32_t aDelay, - bool aSameUri, - bool *allowRefresh) -{ - *allowRefresh = true; - return NS_OK; -} - -//////////////////////////////////////////////////////////////////////////////// -//// nsIWebProgressListener - -NS_IMETHODIMP -nsDownload::OnProgressChange(nsIWebProgress *aWebProgress, - nsIRequest *aRequest, - int32_t aCurSelfProgress, - int32_t aMaxSelfProgress, - int32_t aCurTotalProgress, - int32_t aMaxTotalProgress) -{ - return OnProgressChange64(aWebProgress, aRequest, - aCurSelfProgress, aMaxSelfProgress, - aCurTotalProgress, aMaxTotalProgress); -} - -NS_IMETHODIMP -nsDownload::OnLocationChange(nsIWebProgress *aWebProgress, - nsIRequest *aRequest, nsIURI *aLocation, - uint32_t aFlags) -{ - return NS_OK; -} - -NS_IMETHODIMP -nsDownload::OnStatusChange(nsIWebProgress *aWebProgress, - nsIRequest *aRequest, nsresult aStatus, - const char16_t *aMessage) -{ - if (NS_FAILED(aStatus)) - return FailDownload(aStatus, aMessage); - return NS_OK; -} - -NS_IMETHODIMP -nsDownload::OnStateChange(nsIWebProgress *aWebProgress, - nsIRequest *aRequest, uint32_t aStateFlags, - nsresult aStatus) -{ - MOZ_ASSERT(NS_IsMainThread(), "Must call OnStateChange in main thread"); - - // We don't want to lose access to our member variables - RefPtr kungFuDeathGrip = this; - - // Check if we're starting a request; the NETWORK flag is necessary to not - // pick up the START of *each* file but only for the whole request - if ((aStateFlags & STATE_START) && (aStateFlags & STATE_IS_NETWORK)) { - nsresult rv; - nsCOMPtr channel = do_QueryInterface(aRequest, &rv); - if (NS_SUCCEEDED(rv)) { - uint32_t status; - rv = channel->GetResponseStatus(&status); - // HTTP 450 - Blocked by parental control proxies - if (NS_SUCCEEDED(rv) && status == 450) { - // Cancel using the provided object - (void)Cancel(); - - // Fail the download - (void)SetState(nsIDownloadManager::DOWNLOAD_BLOCKED_PARENTAL); - } - } - } else if ((aStateFlags & STATE_STOP) && (aStateFlags & STATE_IS_NETWORK) && - IsFinishable()) { - // We got both STOP and NETWORK so that means the whole request is done - // (and not just a single file if there are multiple files) - if (NS_SUCCEEDED(aStatus)) { - // We can't completely trust the bytes we've added up because we might be - // missing on some/all of the progress updates (especially from cache). - // Our best bet is the file itself, but if for some reason it's gone or - // if we have multiple files, the next best is what we've calculated. - int64_t fileSize; - nsCOMPtr file; - // We need a nsIFile clone to deal with file size caching issues. :( - nsCOMPtr clone; - if (!mHasMultipleFiles && - NS_SUCCEEDED(GetTargetFile(getter_AddRefs(file))) && - NS_SUCCEEDED(file->Clone(getter_AddRefs(clone))) && - NS_SUCCEEDED(clone->GetFileSize(&fileSize)) && fileSize > 0) { - mCurrBytes = mMaxBytes = fileSize; - - // If we resumed, keep the fact that we did and fix size calculations - if (WasResumed()) - mResumedAt = 0; - } else if (mMaxBytes == -1) { - mMaxBytes = mCurrBytes; - } else { - mCurrBytes = mMaxBytes; - } - - mPercentComplete = 100; - mLastUpdate = PR_Now(); - -#ifdef DOWNLOAD_SCANNER - bool scan = true; - nsCOMPtr prefs(do_GetService(NS_PREFSERVICE_CONTRACTID)); - if (prefs) - (void)prefs->GetBoolPref(PREF_BDM_SCANWHENDONE, &scan); - - if (scan) - (void)SetState(nsIDownloadManager::DOWNLOAD_SCANNING); - else - (void)SetState(nsIDownloadManager::DOWNLOAD_FINISHED); -#else - (void)SetState(nsIDownloadManager::DOWNLOAD_FINISHED); -#endif - } else { - // We failed for some unknown reason -- fail with a generic message - (void)FailDownload(aStatus, nullptr); - } - } - - mDownloadManager->NotifyListenersOnStateChange(aWebProgress, aRequest, - aStateFlags, aStatus, this); - return NS_OK; -} - -NS_IMETHODIMP -nsDownload::OnSecurityChange(nsIWebProgress *aWebProgress, - nsIRequest *aRequest, uint32_t aState) -{ - return NS_OK; -} - -//////////////////////////////////////////////////////////////////////////////// -//// nsIDownload - -NS_IMETHODIMP -nsDownload::Init(nsIURI *aSource, - nsIURI *aTarget, - const nsAString& aDisplayName, - nsIMIMEInfo *aMIMEInfo, - PRTime aStartTime, - nsIFile *aTempFile, - nsICancelable *aCancelable, - bool aIsPrivate) -{ - NS_WARNING("Huh...how did we get here?!"); - return NS_OK; -} - -NS_IMETHODIMP -nsDownload::GetState(int16_t *aState) -{ - *aState = mDownloadState; - return NS_OK; -} - -NS_IMETHODIMP -nsDownload::GetDisplayName(nsAString &aDisplayName) -{ - aDisplayName = mDisplayName; - return NS_OK; -} - -NS_IMETHODIMP -nsDownload::GetCancelable(nsICancelable **aCancelable) -{ - *aCancelable = mCancelable; - NS_IF_ADDREF(*aCancelable); - return NS_OK; -} - -NS_IMETHODIMP -nsDownload::GetTarget(nsIURI **aTarget) -{ - *aTarget = mTarget; - NS_IF_ADDREF(*aTarget); - return NS_OK; + if (exists && writable && directory) { + customDirectory.forget(aResult); + return NS_OK; + } + } + rv = GetDefaultDownloadsDirectory(aResult); + if (NS_SUCCEEDED(rv)) { + (void)prefBranch->SetComplexValue(NS_PREF_DIR, + NS_GET_IID(nsIFile), + *aResult); + } + return rv; + } + break; + } + return NS_ERROR_INVALID_ARG; } NS_IMETHODIMP -nsDownload::GetSource(nsIURI **aSource) +nsDownloadManager::AddDownload(int16_t aDownloadType, + nsIURI *aSource, + nsIURI *aTarget, + const nsAString& aDisplayName, + nsIMIMEInfo *aMIMEInfo, + PRTime aStartTime, + nsIFile *aTempFile, + nsICancelable *aCancelable, + bool aIsPrivate, + nsIDownload **aDownload) { - *aSource = mSource; - NS_IF_ADDREF(*aSource); - return NS_OK; + return NS_ERROR_UNEXPECTED; } NS_IMETHODIMP -nsDownload::GetStartTime(int64_t *aStartTime) +nsDownloadManager::GetDownload(uint32_t aID, nsIDownload **aDownloadItem) { - *aStartTime = mStartTime; - return NS_OK; + return NS_ERROR_UNEXPECTED; } NS_IMETHODIMP -nsDownload::GetPercentComplete(int32_t *aPercentComplete) +nsDownloadManager::GetDownloadByGUID(const nsACString& aGUID, + nsIDownloadManagerResult* aCallback) { - *aPercentComplete = mPercentComplete; - return NS_OK; + return NS_ERROR_UNEXPECTED; } NS_IMETHODIMP -nsDownload::GetAmountTransferred(int64_t *aAmountTransferred) +nsDownloadManager::CancelDownload(uint32_t aID) { - *aAmountTransferred = mCurrBytes + (WasResumed() ? mResumedAt : 0); - return NS_OK; + return NS_ERROR_UNEXPECTED; } NS_IMETHODIMP -nsDownload::GetSize(int64_t *aSize) +nsDownloadManager::RetryDownload(uint32_t aID) { - *aSize = mMaxBytes + (WasResumed() && mMaxBytes != -1 ? mResumedAt : 0); - return NS_OK; + return NS_ERROR_UNEXPECTED; } NS_IMETHODIMP -nsDownload::GetMIMEInfo(nsIMIMEInfo **aMIMEInfo) +nsDownloadManager::RemoveDownload(uint32_t aID) { - *aMIMEInfo = mMIMEInfo; - NS_IF_ADDREF(*aMIMEInfo); - return NS_OK; + return NS_ERROR_UNEXPECTED; } NS_IMETHODIMP -nsDownload::GetTargetFile(nsIFile **aTargetFile) +nsDownloadManager::RemoveDownloadsByTimeframe(int64_t aStartTime, + int64_t aEndTime) { - nsresult rv; - - nsCOMPtr fileURL = do_QueryInterface(mTarget, &rv); - if (NS_FAILED(rv)) { - return rv; - } - - nsCOMPtr file; - rv = fileURL->GetFile(getter_AddRefs(file)); - if (NS_FAILED(rv)) { - return rv; - } - - file.forget(aTargetFile); - return rv; + return NS_ERROR_UNEXPECTED; } NS_IMETHODIMP -nsDownload::GetSpeed(double *aSpeed) +nsDownloadManager::CleanUp() { - *aSpeed = mSpeed; - return NS_OK; + return NS_ERROR_UNEXPECTED; } NS_IMETHODIMP -nsDownload::GetId(uint32_t *aId) +nsDownloadManager::CleanUpPrivate() { - if (mPrivate) { - return NS_ERROR_NOT_AVAILABLE; - } - *aId = mID; - return NS_OK; + return NS_ERROR_UNEXPECTED; } NS_IMETHODIMP -nsDownload::GetGuid(nsACString &aGUID) +nsDownloadManager::GetCanCleanUp(bool *aResult) { - aGUID = mGUID; - return NS_OK; + return NS_ERROR_UNEXPECTED; } NS_IMETHODIMP -nsDownload::GetReferrer(nsIURI **referrer) +nsDownloadManager::GetCanCleanUpPrivate(bool *aResult) { - NS_IF_ADDREF(*referrer = mReferrer); - return NS_OK; + return NS_ERROR_UNEXPECTED; } NS_IMETHODIMP -nsDownload::GetResumable(bool *resumable) +nsDownloadManager::PauseDownload(uint32_t aID) { - *resumable = IsResumable(); - return NS_OK; + return NS_ERROR_UNEXPECTED; } NS_IMETHODIMP -nsDownload::GetIsPrivate(bool *isPrivate) -{ - *isPrivate = mPrivate; - return NS_OK; -} - -//////////////////////////////////////////////////////////////////////////////// -//// nsDownload Helper Functions - -void -nsDownload::Finalize() -{ - // We're stopping, so break the cycle we created at download start - mCancelable = nullptr; - - // Reset values that aren't needed anymore, so the DB can be updated as well - mEntityID.Truncate(); - mTempFile = nullptr; - - // Remove ourself from the active downloads - nsCOMArray& currentDownloads = mPrivate ? - mDownloadManager->mCurrentPrivateDownloads : - mDownloadManager->mCurrentDownloads; - (void)currentDownloads.RemoveObject(this); - - // Make sure we do not automatically resume - mAutoResume = DONT_RESUME; -} - -nsresult -nsDownload::ExecuteDesiredAction() -{ - // nsExternalHelperAppHandler is the only caller of AddDownload that sets a - // tempfile parameter. In this case, execute the desired action according to - // the saved mime info. - if (!mTempFile) { - return NS_OK; - } - - // We need to bail if for some reason the temp file got removed - bool fileExists; - if (NS_FAILED(mTempFile->Exists(&fileExists)) || !fileExists) - return NS_ERROR_FILE_NOT_FOUND; - - // Assume an unknown action is save to disk - nsHandlerInfoAction action = nsIMIMEInfo::saveToDisk; - if (mMIMEInfo) { - nsresult rv = mMIMEInfo->GetPreferredAction(&action); - NS_ENSURE_SUCCESS(rv, rv); - } - - nsresult rv = NS_OK; - switch (action) { - case nsIMIMEInfo::saveToDisk: - // Move the file to the proper location - rv = MoveTempToTarget(); - if (NS_SUCCEEDED(rv)) { - rv = FixTargetPermissions(); - } - break; - case nsIMIMEInfo::useHelperApp: - case nsIMIMEInfo::useSystemDefault: - // For these cases we have to move the file to the target location and - // open with the appropriate application - rv = OpenWithApplication(); - break; - default: - break; - } - - return rv; -} - -nsresult -nsDownload::FixTargetPermissions() -{ - nsCOMPtr target; - nsresult rv = GetTargetFile(getter_AddRefs(target)); - NS_ENSURE_SUCCESS(rv, rv); - - // Set perms according to umask. - nsCOMPtr infoService = - do_GetService("@mozilla.org/system-info;1"); - uint32_t gUserUmask = 0; - rv = infoService->GetPropertyAsUint32(NS_LITERAL_STRING("umask"), - &gUserUmask); - if (NS_SUCCEEDED(rv)) { - (void)target->SetPermissions(0666 & ~gUserUmask); - } - return NS_OK; -} - -nsresult -nsDownload::MoveTempToTarget() -{ - nsCOMPtr target; - nsresult rv = GetTargetFile(getter_AddRefs(target)); - NS_ENSURE_SUCCESS(rv, rv); - - // MoveTo will fail if the file already exists, but we've already obtained - // confirmation from the user that this is OK, so remove it if it exists. - bool fileExists; - if (NS_SUCCEEDED(target->Exists(&fileExists)) && fileExists) { - rv = target->Remove(false); - NS_ENSURE_SUCCESS(rv, rv); - } - - // Extract the new leaf name from the file location - nsAutoString fileName; - rv = target->GetLeafName(fileName); - NS_ENSURE_SUCCESS(rv, rv); - nsCOMPtr dir; - rv = target->GetParent(getter_AddRefs(dir)); - NS_ENSURE_SUCCESS(rv, rv); - rv = mTempFile->MoveTo(dir, fileName); - return rv; -} - -nsresult -nsDownload::OpenWithApplication() -{ - // First move the temporary file to the target location - nsCOMPtr target; - nsresult rv = GetTargetFile(getter_AddRefs(target)); - NS_ENSURE_SUCCESS(rv, rv); - - // Move the temporary file to the target location - rv = MoveTempToTarget(); - NS_ENSURE_SUCCESS(rv, rv); - - bool deleteTempFileOnExit; - nsCOMPtr prefs(do_GetService(NS_PREFSERVICE_CONTRACTID)); - if (!prefs || NS_FAILED(prefs->GetBoolPref(PREF_BH_DELETETEMPFILEONEXIT, - &deleteTempFileOnExit))) { - // No prefservice or no pref set; use default value -#if !defined(XP_MACOSX) - // Mac users have been very verbal about temp files being deleted on - // app exit - they don't like it - but we'll continue to do this on - // other platforms for now. - deleteTempFileOnExit = true; -#else - deleteTempFileOnExit = false; -#endif - } - - // Always schedule files to be deleted at the end of the private browsing - // mode, regardless of the value of the pref. - if (deleteTempFileOnExit || mPrivate) { - - // Make the tmp file readonly so users won't lose changes. - target->SetPermissions(0400); - - // Use the ExternalHelperAppService to push the temporary file to the list - // of files to be deleted on exit. - nsCOMPtr appLauncher(do_GetService - (NS_EXTERNALHELPERAPPSERVICE_CONTRACTID)); - - // Even if we are unable to get this service we return the result - // of LaunchWithFile() which makes more sense. - if (appLauncher) { - if (mPrivate) { - (void)appLauncher->DeleteTemporaryPrivateFileWhenPossible(target); - } else { - (void)appLauncher->DeleteTemporaryFileOnExit(target); - } - } - } - - return mMIMEInfo->LaunchWithFile(target); -} - -void -nsDownload::SetStartTime(int64_t aStartTime) -{ - mStartTime = aStartTime; - mLastUpdate = aStartTime; -} - -void -nsDownload::SetProgressBytes(int64_t aCurrBytes, int64_t aMaxBytes) +nsDownloadManager::ResumeDownload(uint32_t aID) { - mCurrBytes = aCurrBytes; - mMaxBytes = aMaxBytes; - - // Get the real bytes that include resume position - int64_t currBytes, maxBytes; - (void)GetAmountTransferred(&currBytes); - (void)GetSize(&maxBytes); - - if (currBytes == maxBytes) - mPercentComplete = 100; - else if (maxBytes <= 0) - mPercentComplete = -1; - else - mPercentComplete = (int32_t)((double)currBytes / maxBytes * 100 + .5); + return NS_ERROR_UNEXPECTED; } NS_IMETHODIMP -nsDownload::Pause() -{ - if (!IsResumable()) - return NS_ERROR_UNEXPECTED; - - nsresult rv = CancelTransfer(); - NS_ENSURE_SUCCESS(rv, rv); - - return SetState(nsIDownloadManager::DOWNLOAD_PAUSED); -} - -nsresult -nsDownload::CancelTransfer() +nsDownloadManager::GetDBConnection(mozIStorageConnection **aDBConn) { - nsresult rv = NS_OK; - if (mCancelable) { - rv = mCancelable->Cancel(NS_BINDING_ABORTED); - // we're done with this, so break the cycle - mCancelable = nullptr; - } - - return rv; + return NS_ERROR_UNEXPECTED; } NS_IMETHODIMP -nsDownload::Cancel() +nsDownloadManager::GetPrivateDBConnection(mozIStorageConnection **aDBConn) { - // Don't cancel if download is already finished - if (IsFinished()) - return NS_OK; - - // Have the download cancel its connection - (void)CancelTransfer(); - - // Dump the temp file because we know we don't need the file anymore. The - // underlying transfer creating the file doesn't delete the file because it - // can't distinguish between a pause that cancels the transfer or a real - // cancel. - if (mTempFile) { - bool exists; - mTempFile->Exists(&exists); - if (exists) - mTempFile->Remove(false); - } - - nsCOMPtr file; - if (NS_SUCCEEDED(GetTargetFile(getter_AddRefs(file)))) - { - bool exists; - file->Exists(&exists); - if (exists) - file->Remove(false); - } - - nsresult rv = SetState(nsIDownloadManager::DOWNLOAD_CANCELED); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; + return NS_ERROR_UNEXPECTED; } NS_IMETHODIMP -nsDownload::Resume() +nsDownloadManager::AddListener(nsIDownloadProgressListener *aListener) { - if (!IsPaused() || !IsResumable()) - return NS_ERROR_UNEXPECTED; - - nsresult rv; - nsCOMPtr wbp = - do_CreateInstance("@mozilla.org/embedding/browser/nsWebBrowserPersist;1", &rv); - NS_ENSURE_SUCCESS(rv, rv); - - rv = wbp->SetPersistFlags(nsIWebBrowserPersist::PERSIST_FLAGS_APPEND_TO_FILE | - nsIWebBrowserPersist::PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION); - NS_ENSURE_SUCCESS(rv, rv); - - // Create a new channel for the source URI - nsCOMPtr channel; - nsCOMPtr ir(do_QueryInterface(wbp)); - rv = NS_NewChannel(getter_AddRefs(channel), - mSource, - nsContentUtils::GetSystemPrincipal(), - nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL, - nsIContentPolicy::TYPE_OTHER, - nullptr, // aLoadGroup - ir); - - NS_ENSURE_SUCCESS(rv, rv); - - nsCOMPtr pbChannel = do_QueryInterface(channel); - if (pbChannel) { - pbChannel->SetPrivate(mPrivate); - } - - // Make sure we can get a file, either the temporary or the real target, for - // both purposes of file size and a target to write to - nsCOMPtr targetLocalFile(mTempFile); - if (!targetLocalFile) { - rv = GetTargetFile(getter_AddRefs(targetLocalFile)); - NS_ENSURE_SUCCESS(rv, rv); - } - - // Get the file size to be used as an offset, but if anything goes wrong - // along the way, we'll silently restart at 0. - int64_t fileSize; - // We need a nsIFile clone to deal with file size caching issues. :( - nsCOMPtr clone; - if (NS_FAILED(targetLocalFile->Clone(getter_AddRefs(clone))) || - NS_FAILED(clone->GetFileSize(&fileSize))) - fileSize = 0; - - // Set the channel to resume at the right position along with the entityID - nsCOMPtr resumableChannel(do_QueryInterface(channel)); - if (!resumableChannel) - return NS_ERROR_UNEXPECTED; - rv = resumableChannel->ResumeAt(fileSize, mEntityID); - NS_ENSURE_SUCCESS(rv, rv); - - // If we know the max size, we know what it should be when resuming - int64_t maxBytes; - GetSize(&maxBytes); - SetProgressBytes(0, maxBytes != -1 ? maxBytes - fileSize : -1); - // Track where we resumed because progress notifications restart at 0 - mResumedAt = fileSize; - - // Set the referrer - if (mReferrer) { - nsCOMPtr httpChannel(do_QueryInterface(channel)); - if (httpChannel) { - rv = httpChannel->SetReferrer(mReferrer); - NS_ENSURE_SUCCESS(rv, rv); - } - } - - // Creates a cycle that will be broken when the download finishes - mCancelable = wbp; - (void)wbp->SetProgressListener(this); - - // Save the channel using nsIWBP - rv = wbp->SaveChannel(channel, targetLocalFile); - if (NS_FAILED(rv)) { - mCancelable = nullptr; - (void)wbp->SetProgressListener(nullptr); - return rv; - } - - return SetState(nsIDownloadManager::DOWNLOAD_DOWNLOADING); + return NS_ERROR_UNEXPECTED; } NS_IMETHODIMP -nsDownload::Remove() +nsDownloadManager::AddPrivacyAwareListener(nsIDownloadProgressListener *aListener) { - return mDownloadManager->RemoveDownload(mGUID); + return NS_ERROR_UNEXPECTED; } NS_IMETHODIMP -nsDownload::Retry() -{ - return mDownloadManager->RetryDownload(mGUID); -} - -bool -nsDownload::IsPaused() -{ - return mDownloadState == nsIDownloadManager::DOWNLOAD_PAUSED; -} - -bool -nsDownload::IsResumable() -{ - return !mEntityID.IsEmpty(); -} - -bool -nsDownload::WasResumed() -{ - return mResumedAt != -1; -} - -bool -nsDownload::ShouldAutoResume() -{ - return mAutoResume == AUTO_RESUME; -} - -bool -nsDownload::IsFinishable() -{ - return mDownloadState == nsIDownloadManager::DOWNLOAD_NOTSTARTED || - mDownloadState == nsIDownloadManager::DOWNLOAD_QUEUED || - mDownloadState == nsIDownloadManager::DOWNLOAD_DOWNLOADING; -} - -bool -nsDownload::IsFinished() -{ - return mDownloadState == nsIDownloadManager::DOWNLOAD_FINISHED; -} - -nsresult -nsDownload::UpdateDB() -{ - NS_ASSERTION(mID, "Download ID is stored as zero. This is bad!"); - NS_ASSERTION(mDownloadManager, "Egads! We have no download manager!"); - - mozIStorageStatement *stmt = mPrivate ? - mDownloadManager->mUpdatePrivateDownloadStatement : mDownloadManager->mUpdateDownloadStatement; - - nsAutoString tempPath; - if (mTempFile) - (void)mTempFile->GetPath(tempPath); - nsresult rv = stmt->BindStringByName(NS_LITERAL_CSTRING("tempPath"), tempPath); - - rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("startTime"), mStartTime); - NS_ENSURE_SUCCESS(rv, rv); - - rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("endTime"), mLastUpdate); - NS_ENSURE_SUCCESS(rv, rv); - - rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("state"), mDownloadState); - NS_ENSURE_SUCCESS(rv, rv); - - if (mReferrer) { - nsAutoCString referrer; - rv = mReferrer->GetSpec(referrer); - NS_ENSURE_SUCCESS(rv, rv); - rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("referrer"), referrer); - } else { - rv = stmt->BindNullByName(NS_LITERAL_CSTRING("referrer")); - } - NS_ENSURE_SUCCESS(rv, rv); - - rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("entityID"), mEntityID); - NS_ENSURE_SUCCESS(rv, rv); - - int64_t currBytes; - (void)GetAmountTransferred(&currBytes); - rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("currBytes"), currBytes); - NS_ENSURE_SUCCESS(rv, rv); - - int64_t maxBytes; - (void)GetSize(&maxBytes); - rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("maxBytes"), maxBytes); - NS_ENSURE_SUCCESS(rv, rv); - - rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("autoResume"), mAutoResume); - NS_ENSURE_SUCCESS(rv, rv); - - rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("id"), mID); - NS_ENSURE_SUCCESS(rv, rv); - - return stmt->Execute(); -} - -nsresult -nsDownload::FailDownload(nsresult aStatus, const char16_t *aMessage) +nsDownloadManager::RemoveListener(nsIDownloadProgressListener *aListener) { - // Grab the bundle before potentially losing our member variables - nsCOMPtr bundle = mDownloadManager->mBundle; - - (void)SetState(nsIDownloadManager::DOWNLOAD_FAILED); - - // Get title for alert. - nsXPIDLString title; - nsresult rv = bundle->GetStringFromName( - u"downloadErrorAlertTitle", getter_Copies(title)); - NS_ENSURE_SUCCESS(rv, rv); - - // Get a generic message if we weren't supplied one - nsXPIDLString message; - message = aMessage; - if (message.IsEmpty()) { - rv = bundle->GetStringFromName( - u"downloadErrorGeneric", getter_Copies(message)); - NS_ENSURE_SUCCESS(rv, rv); - } - - // Get Download Manager window to be parent of alert - nsCOMPtr wm = - do_GetService(NS_WINDOWMEDIATOR_CONTRACTID, &rv); - NS_ENSURE_SUCCESS(rv, rv); - nsCOMPtr dmWindow; - rv = wm->GetMostRecentWindow(u"Download:Manager", - getter_AddRefs(dmWindow)); - NS_ENSURE_SUCCESS(rv, rv); - - // Show alert - nsCOMPtr prompter = - do_GetService("@mozilla.org/embedcomp/prompt-service;1", &rv); - NS_ENSURE_SUCCESS(rv, rv); - return prompter->Alert(dmWindow, title, message); + return NS_ERROR_UNEXPECTED; } diff --git a/toolkit/components/downloads/nsDownloadManager.h b/toolkit/components/downloads/nsDownloadManager.h index 566e3560add99..f03df33d94843 100644 --- a/toolkit/components/downloads/nsDownloadManager.h +++ b/toolkit/components/downloads/nsDownloadManager.h @@ -6,449 +6,34 @@ #ifndef downloadmanager___h___ #define downloadmanager___h___ -#if defined(XP_WIN) -#define DOWNLOAD_SCANNER -#endif - -#include "nsIDownload.h" #include "nsIDownloadManager.h" #include "nsIDownloadProgressListener.h" #include "nsIFile.h" -#include "nsIMIMEInfo.h" -#include "nsINavHistoryService.h" -#include "nsIObserver.h" -#include "nsIObserverService.h" #include "nsIStringBundle.h" #include "nsISupportsPrimitives.h" -#include "nsWeakReference.h" -#include "nsITimer.h" #include "nsString.h" -#include "mozIDOMWindow.h" -#include "mozStorageHelper.h" -#include "nsAutoPtr.h" -#include "nsCOMArray.h" - -typedef int16_t DownloadState; -typedef int16_t DownloadType; - -class nsIArray; -class nsDownload; - -#ifdef DOWNLOAD_SCANNER -#include "nsDownloadScanner.h" -#endif - -class nsDownloadManager final : public nsIDownloadManager, - public nsINavHistoryObserver, - public nsIObserver, - public nsSupportsWeakReference +class nsDownloadManager final : public nsIDownloadManager { public: NS_DECL_ISUPPORTS NS_DECL_NSIDOWNLOADMANAGER - NS_DECL_NSINAVHISTORYOBSERVER - NS_DECL_NSIOBSERVER nsresult Init(); static nsDownloadManager *GetSingleton(); nsDownloadManager() -#ifdef DOWNLOAD_SCANNER - : mScanner(nullptr) -#endif { } protected: virtual ~nsDownloadManager(); - nsresult InitDB(); - nsresult InitFileDB(); - void CloseAllDBs(); - void CloseDB(mozIStorageConnection* aDBConn, - mozIStorageStatement* aUpdateStmt, - mozIStorageStatement* aGetIdsStmt); - nsresult InitPrivateDB(); - already_AddRefed GetFileDBConnection(nsIFile *dbFile) const; - already_AddRefed GetPrivateDBConnection() const; - nsresult CreateTable(mozIStorageConnection* aDBConn); - - /** - * Fix up the database after a crash such as dealing with previously-active - * downloads. Call this before RestoreActiveDownloads to get the downloads - * fixed here to be auto-resumed. - */ - nsresult RestoreDatabaseState(); - - /** - * Paused downloads that survive across sessions are considered active, so - * rebuild the list of these downloads. - */ - nsresult RestoreActiveDownloads(); - - nsresult GetDownloadFromDB(const nsACString& aGUID, nsDownload **retVal); - nsresult GetDownloadFromDB(uint32_t aID, nsDownload **retVal); - nsresult GetDownloadFromDB(mozIStorageConnection* aDBConn, - mozIStorageStatement* stmt, - nsDownload **retVal); - - /** - * Specially track the active downloads so that we don't need to check - * every download to see if they're in progress. - */ - nsresult AddToCurrentDownloads(nsDownload *aDl); - - void SendEvent(nsDownload *aDownload, const char *aTopic); - - /** - * Adds a download with the specified information to the DB. - * - * @return The id of the download, or 0 if there was an error. - */ - int64_t AddDownloadToDB(const nsAString &aName, - const nsACString &aSource, - const nsACString &aTarget, - const nsAString &aTempPath, - int64_t aStartTime, - int64_t aEndTime, - const nsACString &aMimeType, - const nsACString &aPreferredApp, - nsHandlerInfoAction aPreferredAction, - bool aPrivate, - nsACString &aNewGUID); - - void NotifyListenersOnDownloadStateChange(int16_t aOldState, - nsDownload *aDownload); - void NotifyListenersOnProgressChange(nsIWebProgress *aProgress, - nsIRequest *aRequest, - int64_t aCurSelfProgress, - int64_t aMaxSelfProgress, - int64_t aCurTotalProgress, - int64_t aMaxTotalProgress, - nsDownload *aDownload); - void NotifyListenersOnStateChange(nsIWebProgress *aProgress, - nsIRequest *aRequest, - uint32_t aStateFlags, - nsresult aStatus, - nsDownload *aDownload); - - nsDownload *FindDownload(const nsACString& aGUID); - nsDownload *FindDownload(uint32_t aID); - - /** - * First try to resume the download, and if that fails, retry it. - * - * @param aDl The download to resume and/or retry. - */ - nsresult ResumeRetry(nsDownload *aDl); - - /** - * Pause all active downloads and remember if they should try to auto-resume - * when the download manager starts again. - * - * @param aSetResume Indicate if the downloads that get paused should be set - * as auto-resume. - */ - nsresult PauseAllDownloads(bool aSetResume); - - /** - * Resume all paused downloads unless we're only supposed to do the automatic - * ones; in that case, try to retry them as well if resuming doesn't work. - * - * @param aResumeAll If true, all downloads will be resumed; otherwise, only - * those that are marked as auto-resume will resume. - */ - nsresult ResumeAllDownloads(bool aResumeAll); - - /** - * Stop tracking the active downloads. Only use this when we're about to quit - * the download manager because we destroy our list of active downloads to - * break the dlmgr<->dl cycle. Active downloads that aren't real-paused will - * be canceled. - */ - nsresult RemoveAllDownloads(); - - /** - * Find all downloads from a source URI and delete them. - * - * @param aURI - * The source URI to remove downloads - */ - nsresult RemoveDownloadsForURI(nsIURI *aURI); - - /** - * Callback used for resuming downloads after getting a wake notification. - * - * @param aTimer - * Timer object fired after some delay after a wake notification - * @param aClosure - * nsDownloadManager object used to resume downloads - */ - static void ResumeOnWakeCallback(nsITimer *aTimer, void *aClosure); - nsCOMPtr mResumeOnWakeTimer; - - void ConfirmCancelDownloads(int32_t aCount, - nsISupportsPRBool *aCancelDownloads, - const char16_t *aTitle, - const char16_t *aCancelMessageMultiple, - const char16_t *aCancelMessageSingle, - const char16_t *aDontCancelButton); - - int32_t GetRetentionBehavior(); - - /** - * Type to indicate possible behaviors for active downloads across sessions. - * - * Possible values are: - * QUIT_AND_RESUME - downloads should be auto-resumed - * QUIT_AND_PAUSE - downloads should be paused - * QUIT_AND_CANCEL - downloads should be cancelled - */ - enum QuitBehavior { - QUIT_AND_RESUME = 0, - QUIT_AND_PAUSE = 1, - QUIT_AND_CANCEL = 2 - }; - - /** - * Indicates user-set behavior for active downloads across sessions, - * - * @return value of user-set pref for active download behavior - */ - enum QuitBehavior GetQuitBehavior(); - - void OnEnterPrivateBrowsingMode(); - void OnLeavePrivateBrowsingMode(); - - nsresult RetryDownload(const nsACString& aGUID); - nsresult RetryDownload(nsDownload* dl); - - nsresult RemoveDownload(const nsACString& aGUID); - - nsresult NotifyDownloadRemoval(nsDownload* aRemoved); - - // Virus scanner for windows -#ifdef DOWNLOAD_SCANNER -private: - nsDownloadScanner* mScanner; -#endif - private: - nsresult CleanUp(mozIStorageConnection* aDBConn); - nsresult InitStatements(mozIStorageConnection* aDBConn, - mozIStorageStatement** aUpdateStatement, - mozIStorageStatement** aGetIdsStatement); - nsresult RemoveAllDownloads(nsCOMArray& aDownloads); - nsresult PauseAllDownloads(nsCOMArray& aDownloads, bool aSetResume); - nsresult ResumeAllDownloads(nsCOMArray& aDownloads, bool aResumeAll); - nsresult RemoveDownloadsForURI(mozIStorageStatement* aStatement, nsIURI *aURI); - - bool mUseJSTransfer; - nsCOMArray mListeners; - nsCOMArray mPrivacyAwareListeners; nsCOMPtr mBundle; - nsCOMPtr mDBConn; - nsCOMPtr mPrivateDBConn; - nsCOMArray mCurrentDownloads; - nsCOMArray mCurrentPrivateDownloads; - nsCOMPtr mObserverService; - nsCOMPtr mUpdateDownloadStatement; - nsCOMPtr mUpdatePrivateDownloadStatement; - nsCOMPtr mGetIdsForURIStatement; - nsCOMPtr mGetPrivateIdsForURIStatement; - nsAutoPtr mHistoryTransaction; static nsDownloadManager *gDownloadManagerService; - - friend class nsDownload; -}; - -class nsDownload final : public nsIDownload -{ -public: - NS_DECL_NSIWEBPROGRESSLISTENER - NS_DECL_NSIWEBPROGRESSLISTENER2 - NS_DECL_NSITRANSFER - NS_DECL_NSIDOWNLOAD - NS_DECL_ISUPPORTS - - nsDownload(); - - /** - * This method MUST be called when changing states on a download. It will - * notify the download listener when a change happens. This also updates the - * database, by calling UpdateDB(). - */ - nsresult SetState(DownloadState aState); - -protected: - virtual ~nsDownload(); - - /** - * Finish up the download by breaking reference cycles and clearing unneeded - * data. Additionally, the download removes itself from the download - * manager's list of current downloads. - * - * NOTE: This method removes the cycle created when starting the download, so - * make sure to use kungFuDeathGrip if you want to access member variables. - */ - void Finalize(); - - /** - * For finished resumed downloads that came in from exthandler, perform the - * action that would have been done if the download wasn't resumed. - */ - nsresult ExecuteDesiredAction(); - - /** - * Move the temporary file to the final destination by removing the existing - * dummy target and renaming the temporary. - */ - nsresult MoveTempToTarget(); - - /** - * Set the target file permissions to be appropriate. - */ - nsresult FixTargetPermissions(); - - /** - * Update the start time which also implies the last update time is the same. - */ - void SetStartTime(int64_t aStartTime); - - /** - * Update the amount of bytes transferred and max bytes; and recalculate the - * download percent. - */ - void SetProgressBytes(int64_t aCurrBytes, int64_t aMaxBytes); - - /** - * All this does is cancel the connection that the download is using. It does - * not remove it from the download manager. - */ - nsresult CancelTransfer(); - - /** - * Download is not transferring? - */ - bool IsPaused(); - - /** - * Download can continue from the middle of a transfer? - */ - bool IsResumable(); - - /** - * Download was resumed? - */ - bool WasResumed(); - - /** - * Indicates if the download should try to automatically resume or not. - */ - bool ShouldAutoResume(); - - /** - * Download is in a state to stop and complete the download? - */ - bool IsFinishable(); - - /** - * Download is totally done transferring and all? - */ - bool IsFinished(); - - /** - * Update the DB with the current state of the download including time, - * download state and other values not known when first creating the - * download DB entry. - */ - nsresult UpdateDB(); - - /** - * Fail a download because of a failure status and prompt the provided - * message or use a generic download failure message if nullptr. - */ - nsresult FailDownload(nsresult aStatus, const char16_t *aMessage); - - /** - * Opens the downloaded file with the appropriate application, which is - * either the OS default, MIME type default, or the one selected by the user. - * - * This also adds the temporary file to the "To be deleted on Exit" list, if - * the corresponding user preference is set (except on OS X). - * - * This function was adopted from nsExternalAppHandler::OpenWithApplication - * (uriloader/exthandler/nsExternalHelperAppService.cpp). - */ - nsresult OpenWithApplication(); - - nsDownloadManager *mDownloadManager; - nsCOMPtr mTarget; - -private: - nsString mDisplayName; - nsCString mEntityID; - nsCString mGUID; - - nsCOMPtr mSource; - nsCOMPtr mReferrer; - nsCOMPtr mCancelable; - nsCOMPtr mRequest; - nsCOMPtr mTempFile; - nsCOMPtr mMIMEInfo; - - DownloadState mDownloadState; - - uint32_t mID; - int32_t mPercentComplete; - - /** - * These bytes are based on the position of where the request started, so 0 - * doesn't necessarily mean we have nothing. Use GetAmountTransferred and - * GetSize for the real transferred amount and size. - */ - int64_t mCurrBytes; - int64_t mMaxBytes; - - PRTime mStartTime; - PRTime mLastUpdate; - int64_t mResumedAt; - double mSpeed; - - bool mHasMultipleFiles; - bool mPrivate; - - /** - * Track various states of the download trying to auto-resume when starting - * the download manager or restoring from a crash. - * - * DONT_RESUME: Don't automatically resume the download - * AUTO_RESUME: Automaically resume the download - */ - enum AutoResume { DONT_RESUME, AUTO_RESUME }; - AutoResume mAutoResume; - - /** - * Stores the SHA-256 hash associated with the downloaded file. - */ - nsCString mHash; - - /** - * Stores the certificate chains in an nsIArray of nsIX509CertList of - * nsIX509Cert, if this binary is signed. - */ - nsCOMPtr mSignatureInfo; - - /** - * Stores the redirects that led to this download in an nsIArray of - * nsIPrincipal. - */ - nsCOMPtr mRedirects; - - friend class nsDownloadManager; }; #endif diff --git a/toolkit/components/downloads/nsDownloadProxy.h b/toolkit/components/downloads/nsDownloadProxy.h deleted file mode 100644 index ca48c9dadb3d2..0000000000000 --- a/toolkit/components/downloads/nsDownloadProxy.h +++ /dev/null @@ -1,179 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* 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/. */ - -#ifndef downloadproxy___h___ -#define downloadproxy___h___ - -#include "nsIDownloadManager.h" -#include "nsIPrefBranch.h" -#include "nsIPrefService.h" -#include "nsIMIMEInfo.h" -#include "nsIFileURL.h" -#include "nsIDownloadManagerUI.h" - -#define PREF_BDM_SHOWWHENSTARTING "browser.download.manager.showWhenStarting" -#define PREF_BDM_FOCUSWHENSTARTING "browser.download.manager.focusWhenStarting" - -// This class only exists because nsDownload cannot inherit from nsITransfer -// directly. The reason for this is that nsDownloadManager (incorrectly) keeps -// an nsCOMArray of nsDownloads, and nsCOMArray is only intended for use with -// abstract classes. Using a concrete class that multiply inherits from classes -// deriving from nsISupports will throw ambiguous base class errors. -class nsDownloadProxy : public nsITransfer -{ -protected: - - virtual ~nsDownloadProxy() { } - -public: - - nsDownloadProxy() { } - - NS_DECL_ISUPPORTS - - NS_IMETHOD Init(nsIURI* aSource, - nsIURI* aTarget, - const nsAString& aDisplayName, - nsIMIMEInfo *aMIMEInfo, - PRTime aStartTime, - nsIFile* aTempFile, - nsICancelable* aCancelable, - bool aIsPrivate) override { - nsresult rv; - nsCOMPtr dm = do_GetService("@mozilla.org/download-manager;1", &rv); - NS_ENSURE_SUCCESS(rv, rv); - - rv = dm->AddDownload(nsIDownloadManager::DOWNLOAD_TYPE_DOWNLOAD, aSource, - aTarget, aDisplayName, aMIMEInfo, aStartTime, - aTempFile, aCancelable, aIsPrivate, - getter_AddRefs(mInner)); - NS_ENSURE_SUCCESS(rv, rv); - - nsCOMPtr prefs = do_GetService("@mozilla.org/preferences-service;1", &rv); - NS_ENSURE_SUCCESS(rv, rv); - nsCOMPtr branch = do_QueryInterface(prefs); - - bool showDM = true; - if (branch) - branch->GetBoolPref(PREF_BDM_SHOWWHENSTARTING, &showDM); - - if (showDM) { - nsCOMPtr dmui = - do_GetService("@mozilla.org/download-manager-ui;1", &rv); - NS_ENSURE_SUCCESS(rv, rv); - - bool visible; - rv = dmui->GetVisible(&visible); - NS_ENSURE_SUCCESS(rv, rv); - - bool focusWhenStarting = true; - if (branch) - (void)branch->GetBoolPref(PREF_BDM_FOCUSWHENSTARTING, &focusWhenStarting); - - if (visible && !focusWhenStarting) - return NS_OK; - - return dmui->Show(nullptr, mInner, nsIDownloadManagerUI::REASON_NEW_DOWNLOAD, aIsPrivate); - } - return rv; - } - - NS_IMETHOD OnStateChange(nsIWebProgress* aWebProgress, - nsIRequest* aRequest, uint32_t aStateFlags, - nsresult aStatus) override - { - NS_ENSURE_TRUE(mInner, NS_ERROR_NOT_INITIALIZED); - return mInner->OnStateChange(aWebProgress, aRequest, aStateFlags, aStatus); - } - - NS_IMETHOD OnStatusChange(nsIWebProgress *aWebProgress, - nsIRequest *aRequest, nsresult aStatus, - const char16_t *aMessage) override - { - NS_ENSURE_TRUE(mInner, NS_ERROR_NOT_INITIALIZED); - return mInner->OnStatusChange(aWebProgress, aRequest, aStatus, aMessage); - } - - NS_IMETHOD OnLocationChange(nsIWebProgress *aWebProgress, - nsIRequest *aRequest, nsIURI *aLocation, - uint32_t aFlags) override - { - NS_ENSURE_TRUE(mInner, NS_ERROR_NOT_INITIALIZED); - return mInner->OnLocationChange(aWebProgress, aRequest, aLocation, aFlags); - } - - NS_IMETHOD OnProgressChange(nsIWebProgress *aWebProgress, - nsIRequest *aRequest, - int32_t aCurSelfProgress, - int32_t aMaxSelfProgress, - int32_t aCurTotalProgress, - int32_t aMaxTotalProgress) override - { - NS_ENSURE_TRUE(mInner, NS_ERROR_NOT_INITIALIZED); - return mInner->OnProgressChange(aWebProgress, aRequest, - aCurSelfProgress, - aMaxSelfProgress, - aCurTotalProgress, - aMaxTotalProgress); - } - - NS_IMETHOD OnProgressChange64(nsIWebProgress *aWebProgress, - nsIRequest *aRequest, - int64_t aCurSelfProgress, - int64_t aMaxSelfProgress, - int64_t aCurTotalProgress, - int64_t aMaxTotalProgress) override - { - NS_ENSURE_TRUE(mInner, NS_ERROR_NOT_INITIALIZED); - return mInner->OnProgressChange64(aWebProgress, aRequest, - aCurSelfProgress, - aMaxSelfProgress, - aCurTotalProgress, - aMaxTotalProgress); - } - - NS_IMETHOD OnRefreshAttempted(nsIWebProgress *aWebProgress, - nsIURI *aUri, - int32_t aDelay, - bool aSameUri, - bool *allowRefresh) override - { - *allowRefresh = true; - return NS_OK; - } - - NS_IMETHOD OnSecurityChange(nsIWebProgress *aWebProgress, - nsIRequest *aRequest, uint32_t aState) override - { - NS_ENSURE_TRUE(mInner, NS_ERROR_NOT_INITIALIZED); - return mInner->OnSecurityChange(aWebProgress, aRequest, aState); - } - - NS_IMETHOD SetSha256Hash(const nsACString& aHash) override - { - NS_ENSURE_TRUE(mInner, NS_ERROR_NOT_INITIALIZED); - return mInner->SetSha256Hash(aHash); - } - - NS_IMETHOD SetSignatureInfo(nsIArray* aSignatureInfo) override - { - NS_ENSURE_TRUE(mInner, NS_ERROR_NOT_INITIALIZED); - return mInner->SetSignatureInfo(aSignatureInfo); - } - - NS_IMETHOD SetRedirects(nsIArray* aRedirects) override - { - NS_ENSURE_TRUE(mInner, NS_ERROR_NOT_INITIALIZED); - return mInner->SetRedirects(aRedirects); - } - -private: - nsCOMPtr mInner; -}; - -NS_IMPL_ISUPPORTS(nsDownloadProxy, nsITransfer, - nsIWebProgressListener, nsIWebProgressListener2) - -#endif diff --git a/toolkit/components/downloads/nsDownloadScanner.cpp b/toolkit/components/downloads/nsDownloadScanner.cpp deleted file mode 100644 index ce4f8febbe740..0000000000000 --- a/toolkit/components/downloads/nsDownloadScanner.cpp +++ /dev/null @@ -1,728 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: se cin sw=2 ts=2 et : */ -/* 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 "nsDownloadScanner.h" -#include -#include -#include "nsDownloadManager.h" -#include "nsIXULAppInfo.h" -#include "nsXULAppAPI.h" -#include "nsIPrefService.h" -#include "nsNetUtil.h" -#include "prtime.h" -#include "nsDeque.h" -#include "nsIFileURL.h" -#include "nsIPrefBranch.h" -#include "nsXPCOMCIDInternal.h" - -/** - * Code overview - * - * Download scanner attempts to make use of one of two different virus - * scanning interfaces available on Windows - IOfficeAntiVirus (Windows - * 95/NT 4 and IE 5) and IAttachmentExecute (XPSP2 and up). The latter - * interface supports calling IOfficeAntiVirus internally, while also - * adding support for XPSP2+ ADS forks which define security related - * prompting on downloaded content. - * - * Both interfaces are synchronous and can take a while, so it is not a - * good idea to call either from the main thread. Some antivirus scanners can - * take a long time to scan or the call might block while the scanner shows - * its UI so if the user were to download many files that finished around the - * same time, they would have to wait a while if the scanning were done on - * exactly one other thread. Since the overhead of creating a thread is - * relatively small compared to the time it takes to download a file and scan - * it, a new thread is spawned for each download that is to be scanned. Since - * most of the mozilla codebase is not threadsafe, all the information needed - * for the scanner is gathered in the main thread in nsDownloadScanner::Scan::Start. - * The only function of nsDownloadScanner::Scan which is invoked on another - * thread is DoScan. - * - * Watchdog overview - * - * The watchdog is used internally by the scanner. It maintains a queue of - * current download scans. In a separate thread, it dequeues the oldest scan - * and waits on that scan's thread with a timeout given by WATCHDOG_TIMEOUT - * (default is 30 seconds). If the wait times out, then the watchdog notifies - * the Scan that it has timed out. If the scan really has timed out, then the - * Scan object will dispatch its run method to the main thread; this will - * release the watchdog thread's addref on the Scan. If it has not timed out - * (i.e. the Scan just finished in time), then the watchdog dispatches a - * ReleaseDispatcher to release its ref of the Scan on the main thread. - * - * In order to minimize execution time, there are two events used to notify the - * watchdog thread of a non-empty queue and a quit event. Every blocking wait - * that the watchdog thread does waits on the quit event; this lets the thread - * quickly exit when shutting down. Also, the download scan queue will be empty - * most of the time; rather than use a spin loop, a simple event is triggered - * by the main thread when a new scan is added to an empty queue. When the - * watchdog thread knows that it has run out of elements in the queue, it will - * wait on the new item event. - * - * Memory/resource leaks due to timeout: - * In the event of a timeout, the thread must remain alive; terminating it may - * very well cause the antivirus scanner to crash or be put into an - * inconsistent state; COM resources may also not be cleaned up. The downside - * is that we need to leave the thread running; suspending it may lead to a - * deadlock. Because the scan call may be ongoing, it may be dependent on the - * memory referenced by the MSOAVINFO structure, so we cannot free mName, mPath - * or mOrigin; this means that we cannot free the Scan object since doing so - * will deallocate that memory. Note that mDownload is set to null upon timeout - * or completion, so the download itself is never leaked. If the scan does - * eventually complete, then the all the memory and resources will be freed. - * It is possible, however extremely rare, that in the event of a timeout, the - * mStateSync critical section will leak its event; this will happen only if - * the scanning thread, watchdog thread or main thread try to enter the - * critical section when one of the others is already in it. - * - * Reasoning for CheckAndSetState - there exists a race condition between the time when - * either the timeout or normal scan sets the state and when Scan::Run is - * executed on the main thread. Ex: mStatus could be set by Scan::DoScan* which - * then queues a dispatch on the main thread. Before that dispatch is executed, - * the timeout code fires and sets mStatus to AVSCAN_TIMEDOUT which then queues - * its dispatch to the main thread (the same function as DoScan*). Both - * dispatches run and both try to set the download state to AVSCAN_TIMEDOUT - * which is incorrect. - * - * There are 5 possible outcomes of the virus scan: - * AVSCAN_GOOD => the file is clean - * AVSCAN_BAD => the file has a virus - * AVSCAN_UGLY => the file had a virus, but it was cleaned - * AVSCAN_FAILED => something else went wrong with the virus scanner. - * AVSCAN_TIMEDOUT => the scan (thread setup + execution) took too long - * - * Both the good and ugly states leave the user with a benign file, so they - * transition to the finished state. Bad files are sent to the blocked state. - * The failed and timedout states transition to finished downloads. - * - * Possible Future enhancements: - * * Create an interface for scanning files in general - * * Make this a service - * * Get antivirus scanner status via WMI/registry - */ - -// IAttachementExecute supports user definable settings for certain -// security related prompts. This defines a general GUID for use in -// all projects. Individual projects can define an individual guid -// if they want to. -#ifndef MOZ_VIRUS_SCANNER_PROMPT_GUID -#define MOZ_VIRUS_SCANNER_PROMPT_GUID \ - { 0xb50563d1, 0x16b6, 0x43c2, { 0xa6, 0x6a, 0xfa, 0xe6, 0xd2, 0x11, 0xf2, \ - 0xea } } -#endif -static const GUID GUID_MozillaVirusScannerPromptGeneric = - MOZ_VIRUS_SCANNER_PROMPT_GUID; - -// Initial timeout is 30 seconds -#define WATCHDOG_TIMEOUT (30*PR_USEC_PER_SEC) - -// Maximum length for URI's passed into IAE -#define MAX_IAEURILENGTH 1683 - -class nsDownloadScannerWatchdog -{ - typedef nsDownloadScanner::Scan Scan; -public: - nsDownloadScannerWatchdog(); - ~nsDownloadScannerWatchdog(); - - nsresult Init(); - nsresult Shutdown(); - - void Watch(Scan *scan); -private: - static unsigned int __stdcall WatchdogThread(void *p); - CRITICAL_SECTION mQueueSync; - nsDeque mScanQueue; - HANDLE mThread; - HANDLE mNewItemEvent; - HANDLE mQuitEvent; -}; - -nsDownloadScanner::nsDownloadScanner() : - mAESExists(false) -{ -} - -// This destructor appeases the compiler; it would otherwise complain about an -// incomplete type for nsDownloadWatchdog in the instantiation of -// nsAutoPtr::~nsAutoPtr -// Plus, it's a handy location to call nsDownloadScannerWatchdog::Shutdown from -nsDownloadScanner::~nsDownloadScanner() { - if (mWatchdog) - (void)mWatchdog->Shutdown(); -} - -nsresult -nsDownloadScanner::Init() -{ - // This CoInitialize/CoUninitialize pattern seems to be common in the Mozilla - // codebase. All other COM calls/objects are made on different threads. - nsresult rv = NS_OK; - CoInitialize(nullptr); - - if (!IsAESAvailable()) { - CoUninitialize(); - return NS_ERROR_NOT_AVAILABLE; - } - - mAESExists = true; - - // Initialize scanning - mWatchdog = new nsDownloadScannerWatchdog(); - if (mWatchdog) { - rv = mWatchdog->Init(); - if (FAILED(rv)) - mWatchdog = nullptr; - } else { - rv = NS_ERROR_OUT_OF_MEMORY; - } - - if (NS_FAILED(rv)) - return rv; - - return rv; -} - -bool -nsDownloadScanner::IsAESAvailable() -{ - // Try to instantiate IAE to see if it's available. - RefPtr ae; - HRESULT hr; - hr = CoCreateInstance(CLSID_AttachmentServices, nullptr, CLSCTX_INPROC, - IID_IAttachmentExecute, getter_AddRefs(ae)); - if (FAILED(hr)) { - NS_WARNING("Could not instantiate attachment execution service\n"); - return false; - } - return true; -} - -// If IAttachementExecute is available, use the CheckPolicy call to find out -// if this download should be prevented due to Security Zone Policy settings. -AVCheckPolicyState -nsDownloadScanner::CheckPolicy(nsIURI *aSource, nsIURI *aTarget) -{ - nsresult rv; - - if (!mAESExists || !aSource || !aTarget) - return AVPOLICY_DOWNLOAD; - - nsAutoCString source; - rv = aSource->GetSpec(source); - if (NS_FAILED(rv)) - return AVPOLICY_DOWNLOAD; - - nsCOMPtr fileUrl(do_QueryInterface(aTarget)); - if (!fileUrl) - return AVPOLICY_DOWNLOAD; - - nsCOMPtr theFile; - nsAutoString aFileName; - if (NS_FAILED(fileUrl->GetFile(getter_AddRefs(theFile))) || - NS_FAILED(theFile->GetLeafName(aFileName))) - return AVPOLICY_DOWNLOAD; - - // IAttachementExecute prohibits src data: schemes by default but we - // support them. If this is a data src, skip off doing a policy check. - // (The file will still be scanned once it lands on the local system.) - bool isDataScheme(false); - nsCOMPtr innerURI = NS_GetInnermostURI(aSource); - if (innerURI) - (void)innerURI->SchemeIs("data", &isDataScheme); - if (isDataScheme) - return AVPOLICY_DOWNLOAD; - - RefPtr ae; - HRESULT hr; - hr = CoCreateInstance(CLSID_AttachmentServices, nullptr, CLSCTX_INPROC, - IID_IAttachmentExecute, getter_AddRefs(ae)); - if (FAILED(hr)) - return AVPOLICY_DOWNLOAD; - - (void)ae->SetClientGuid(GUID_MozillaVirusScannerPromptGeneric); - (void)ae->SetSource(NS_ConvertUTF8toUTF16(source).get()); - (void)ae->SetFileName(aFileName.get()); - - // Any failure means the file download/exec will be blocked by the system. - // S_OK or S_FALSE imply it's ok. - hr = ae->CheckPolicy(); - - if (hr == S_OK) - return AVPOLICY_DOWNLOAD; - - if (hr == S_FALSE) - return AVPOLICY_PROMPT; - - if (hr == E_INVALIDARG) - return AVPOLICY_PROMPT; - - return AVPOLICY_BLOCKED; -} - -#ifndef THREAD_MODE_BACKGROUND_BEGIN -#define THREAD_MODE_BACKGROUND_BEGIN 0x00010000 -#endif - -#ifndef THREAD_MODE_BACKGROUND_END -#define THREAD_MODE_BACKGROUND_END 0x00020000 -#endif - -unsigned int __stdcall -nsDownloadScanner::ScannerThreadFunction(void *p) -{ - HANDLE currentThread = GetCurrentThread(); - NS_ASSERTION(!NS_IsMainThread(), "Antivirus scan should not be run on the main thread"); - nsDownloadScanner::Scan *scan = static_cast(p); - if (!SetThreadPriority(currentThread, THREAD_MODE_BACKGROUND_BEGIN)) - (void)SetThreadPriority(currentThread, THREAD_PRIORITY_IDLE); - scan->DoScan(); - (void)SetThreadPriority(currentThread, THREAD_MODE_BACKGROUND_END); - _endthreadex(0); - return 0; -} - -// The sole purpose of this class is to release an object on the main thread -// It assumes that its creator will addref it and it will release itself on -// the main thread too -class ReleaseDispatcher : public mozilla::Runnable { -public: - explicit ReleaseDispatcher(nsISupports *ptr) - : mPtr(ptr) {} - NS_IMETHOD Run(); -private: - nsISupports *mPtr; -}; - -nsresult ReleaseDispatcher::Run() { - NS_ASSERTION(NS_IsMainThread(), "Antivirus scan release dispatch should be run on the main thread"); - NS_RELEASE(mPtr); - NS_RELEASE_THIS(); - return NS_OK; -} - -nsDownloadScanner::Scan::Scan(nsDownloadScanner *scanner, nsDownload *download) - : mDLScanner(scanner), mThread(nullptr), - mDownload(download), mStatus(AVSCAN_NOTSTARTED), - mSkipSource(false) -{ - InitializeCriticalSection(&mStateSync); -} - -nsDownloadScanner::Scan::~Scan() { - DeleteCriticalSection(&mStateSync); -} - -nsresult -nsDownloadScanner::Scan::Start() -{ - mStartTime = PR_Now(); - - mThread = (HANDLE)_beginthreadex(nullptr, 0, ScannerThreadFunction, - this, CREATE_SUSPENDED, nullptr); - if (!mThread) - return NS_ERROR_OUT_OF_MEMORY; - - nsresult rv = NS_OK; - - // Get the path to the file on disk - nsCOMPtr file; - rv = mDownload->GetTargetFile(getter_AddRefs(file)); - NS_ENSURE_SUCCESS(rv, rv); - rv = file->GetPath(mPath); - NS_ENSURE_SUCCESS(rv, rv); - - // Grab the app name - nsCOMPtr appinfo = - do_GetService(XULAPPINFO_SERVICE_CONTRACTID, &rv); - NS_ENSURE_SUCCESS(rv, rv); - - nsAutoCString name; - rv = appinfo->GetName(name); - NS_ENSURE_SUCCESS(rv, rv); - CopyUTF8toUTF16(name, mName); - - // Get the origin - nsCOMPtr uri; - rv = mDownload->GetSource(getter_AddRefs(uri)); - NS_ENSURE_SUCCESS(rv, rv); - - nsAutoCString origin; - rv = uri->GetSpec(origin); - NS_ENSURE_SUCCESS(rv, rv); - - // Certain virus interfaces do not like extremely long uris. - // Chop off the path and cgi data and just pass the base domain. - if (origin.Length() > MAX_IAEURILENGTH) { - rv = uri->GetPrePath(origin); - NS_ENSURE_SUCCESS(rv, rv); - } - - CopyUTF8toUTF16(origin, mOrigin); - - // We count https/ftp/http as an http download - bool isHttp(false), isFtp(false), isHttps(false); - nsCOMPtr innerURI = NS_GetInnermostURI(uri); - if (!innerURI) innerURI = uri; - (void)innerURI->SchemeIs("http", &isHttp); - (void)innerURI->SchemeIs("ftp", &isFtp); - (void)innerURI->SchemeIs("https", &isHttps); - mIsHttpDownload = isHttp || isFtp || isHttps; - - // IAttachementExecute prohibits src data: schemes by default but we - // support them. Mark the download if it's a data scheme, so we - // can skip off supplying the src to IAttachementExecute when we scan - // the resulting file. - (void)innerURI->SchemeIs("data", &mSkipSource); - - // ResumeThread returns the previous suspend count - if (1 != ::ResumeThread(mThread)) { - CloseHandle(mThread); - return NS_ERROR_UNEXPECTED; - } - return NS_OK; -} - -nsresult -nsDownloadScanner::Scan::Run() -{ - NS_ASSERTION(NS_IsMainThread(), "Antivirus scan dispatch should be run on the main thread"); - - // Cleanup our thread - if (mStatus != AVSCAN_TIMEDOUT) - WaitForSingleObject(mThread, INFINITE); - CloseHandle(mThread); - - DownloadState downloadState = 0; - EnterCriticalSection(&mStateSync); - switch (mStatus) { - case AVSCAN_BAD: - downloadState = nsIDownloadManager::DOWNLOAD_DIRTY; - break; - default: - case AVSCAN_FAILED: - case AVSCAN_GOOD: - case AVSCAN_UGLY: - case AVSCAN_TIMEDOUT: - downloadState = nsIDownloadManager::DOWNLOAD_FINISHED; - break; - } - LeaveCriticalSection(&mStateSync); - // Download will be null if we already timed out - if (mDownload) - (void)mDownload->SetState(downloadState); - - // Clean up some other variables - // In the event of a timeout, our destructor won't be called - mDownload = nullptr; - - NS_RELEASE_THIS(); - return NS_OK; -} - -static DWORD -ExceptionFilterFunction(DWORD exceptionCode) { - switch(exceptionCode) { - case EXCEPTION_ACCESS_VIOLATION: - case EXCEPTION_ILLEGAL_INSTRUCTION: - case EXCEPTION_IN_PAGE_ERROR: - case EXCEPTION_PRIV_INSTRUCTION: - case EXCEPTION_STACK_OVERFLOW: - return EXCEPTION_EXECUTE_HANDLER; - default: - return EXCEPTION_CONTINUE_SEARCH; - } -} - -bool -nsDownloadScanner::Scan::DoScanAES() -{ - // This warning is for the destructor of ae which will not be invoked in the - // event of a win32 exception -#pragma warning(disable: 4509) - HRESULT hr; - RefPtr ae; - MOZ_SEH_TRY { - hr = CoCreateInstance(CLSID_AttachmentServices, nullptr, CLSCTX_ALL, - IID_IAttachmentExecute, getter_AddRefs(ae)); - } MOZ_SEH_EXCEPT(ExceptionFilterFunction(GetExceptionCode())) { - return CheckAndSetState(AVSCAN_NOTSTARTED,AVSCAN_FAILED); - } - - // If we (somehow) already timed out, then don't bother scanning - if (CheckAndSetState(AVSCAN_SCANNING, AVSCAN_NOTSTARTED)) { - AVScanState newState; - if (SUCCEEDED(hr)) { - bool gotException = false; - MOZ_SEH_TRY { - (void)ae->SetClientGuid(GUID_MozillaVirusScannerPromptGeneric); - (void)ae->SetLocalPath(mPath.get()); - // Provide the src for everything but data: schemes. - if (!mSkipSource) - (void)ae->SetSource(mOrigin.get()); - - // Save() will invoke the scanner - hr = ae->Save(); - } MOZ_SEH_EXCEPT(ExceptionFilterFunction(GetExceptionCode())) { - gotException = true; - } - - MOZ_SEH_TRY { - ae = nullptr; - } MOZ_SEH_EXCEPT(ExceptionFilterFunction(GetExceptionCode())) { - gotException = true; - } - - if(gotException) { - newState = AVSCAN_FAILED; - } - else if (SUCCEEDED(hr)) { // Passed the scan - newState = AVSCAN_GOOD; - } - else if (HRESULT_CODE(hr) == ERROR_FILE_NOT_FOUND) { - NS_WARNING("Downloaded file disappeared before it could be scanned"); - newState = AVSCAN_FAILED; - } - else if (hr == E_INVALIDARG) { - NS_WARNING("IAttachementExecute returned invalid argument error"); - newState = AVSCAN_FAILED; - } - else { - newState = AVSCAN_UGLY; - } - } - else { - newState = AVSCAN_FAILED; - } - return CheckAndSetState(newState, AVSCAN_SCANNING); - } - return false; -} -#pragma warning(default: 4509) - -void -nsDownloadScanner::Scan::DoScan() -{ - CoInitialize(nullptr); - - if (DoScanAES()) { - // We need to do a few more things on the main thread - NS_DispatchToMainThread(this); - } else { - // We timed out, so just release - ReleaseDispatcher* releaser = new ReleaseDispatcher(ToSupports(this)); - if(releaser) { - NS_ADDREF(releaser); - NS_DispatchToMainThread(releaser); - } - } - - MOZ_SEH_TRY { - CoUninitialize(); - } MOZ_SEH_EXCEPT(ExceptionFilterFunction(GetExceptionCode())) { - // Not much we can do at this point... - } -} - -HANDLE -nsDownloadScanner::Scan::GetWaitableThreadHandle() const -{ - HANDLE targetHandle = INVALID_HANDLE_VALUE; - (void)DuplicateHandle(GetCurrentProcess(), mThread, - GetCurrentProcess(), &targetHandle, - SYNCHRONIZE, // Only allow clients to wait on this handle - FALSE, // cannot be inherited by child processes - 0); - return targetHandle; -} - -bool -nsDownloadScanner::Scan::NotifyTimeout() -{ - bool didTimeout = CheckAndSetState(AVSCAN_TIMEDOUT, AVSCAN_SCANNING) || - CheckAndSetState(AVSCAN_TIMEDOUT, AVSCAN_NOTSTARTED); - if (didTimeout) { - // We need to do a few more things on the main thread - NS_DispatchToMainThread(this); - } - return didTimeout; -} - -bool -nsDownloadScanner::Scan::CheckAndSetState(AVScanState newState, AVScanState expectedState) { - bool gotExpectedState = false; - EnterCriticalSection(&mStateSync); - if((gotExpectedState = (mStatus == expectedState))) - mStatus = newState; - LeaveCriticalSection(&mStateSync); - return gotExpectedState; -} - -nsresult -nsDownloadScanner::ScanDownload(nsDownload *download) -{ - if (!mAESExists) - return NS_ERROR_NOT_AVAILABLE; - - // No ref ptr, see comment below - Scan *scan = new Scan(this, download); - if (!scan) - return NS_ERROR_OUT_OF_MEMORY; - - NS_ADDREF(scan); - - nsresult rv = scan->Start(); - - // Note that we only release upon error. On success, the scan is passed off - // to a new thread. It is eventually released in Scan::Run on the main thread. - if (NS_FAILED(rv)) - NS_RELEASE(scan); - else - // Notify the watchdog - mWatchdog->Watch(scan); - - return rv; -} - -nsDownloadScannerWatchdog::nsDownloadScannerWatchdog() - : mNewItemEvent(nullptr), mQuitEvent(nullptr) { - InitializeCriticalSection(&mQueueSync); -} -nsDownloadScannerWatchdog::~nsDownloadScannerWatchdog() { - DeleteCriticalSection(&mQueueSync); -} - -nsresult -nsDownloadScannerWatchdog::Init() { - // Both events are auto-reset - mNewItemEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr); - if (INVALID_HANDLE_VALUE == mNewItemEvent) - return NS_ERROR_OUT_OF_MEMORY; - mQuitEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr); - if (INVALID_HANDLE_VALUE == mQuitEvent) { - (void)CloseHandle(mNewItemEvent); - return NS_ERROR_OUT_OF_MEMORY; - } - - // This thread is always running, however it will be asleep - // for most of the dlmgr's lifetime - mThread = (HANDLE)_beginthreadex(nullptr, 0, WatchdogThread, - this, 0, nullptr); - if (!mThread) { - (void)CloseHandle(mNewItemEvent); - (void)CloseHandle(mQuitEvent); - return NS_ERROR_OUT_OF_MEMORY; - } - - return NS_OK; -} - -nsresult -nsDownloadScannerWatchdog::Shutdown() { - // Tell the watchdog thread to quite - (void)SetEvent(mQuitEvent); - (void)WaitForSingleObject(mThread, INFINITE); - (void)CloseHandle(mThread); - // Manually clear and release the queued scans - while (mScanQueue.GetSize() != 0) { - Scan *scan = reinterpret_cast(mScanQueue.Pop()); - NS_RELEASE(scan); - } - (void)CloseHandle(mNewItemEvent); - (void)CloseHandle(mQuitEvent); - return NS_OK; -} - -void -nsDownloadScannerWatchdog::Watch(Scan *scan) { - bool wasEmpty; - // Note that there is no release in this method - // The scan will be released by the watchdog ALWAYS on the main thread - // when either the watchdog thread processes the scan or the watchdog - // is shut down - NS_ADDREF(scan); - EnterCriticalSection(&mQueueSync); - wasEmpty = mScanQueue.GetSize()==0; - mScanQueue.Push(scan); - LeaveCriticalSection(&mQueueSync); - // If the queue was empty, then the watchdog thread is/will be asleep - if (wasEmpty) - (void)SetEvent(mNewItemEvent); -} - -unsigned int -__stdcall -nsDownloadScannerWatchdog::WatchdogThread(void *p) { - NS_ASSERTION(!NS_IsMainThread(), "Antivirus scan watchdog should not be run on the main thread"); - nsDownloadScannerWatchdog *watchdog = (nsDownloadScannerWatchdog*)p; - HANDLE waitHandles[3] = {watchdog->mNewItemEvent, watchdog->mQuitEvent, INVALID_HANDLE_VALUE}; - DWORD waitStatus; - DWORD queueItemsLeft = 0; - // Loop until quit event or error - while (0 != queueItemsLeft || - ((WAIT_OBJECT_0 + 1) != - (waitStatus = - WaitForMultipleObjects(2, waitHandles, FALSE, INFINITE)) && - waitStatus != WAIT_FAILED)) { - Scan *scan = nullptr; - PRTime startTime, expectedEndTime, now; - DWORD waitTime; - - // Pop scan from queue - EnterCriticalSection(&watchdog->mQueueSync); - scan = reinterpret_cast(watchdog->mScanQueue.Pop()); - queueItemsLeft = watchdog->mScanQueue.GetSize(); - LeaveCriticalSection(&watchdog->mQueueSync); - - // Calculate expected end time - startTime = scan->GetStartTime(); - expectedEndTime = WATCHDOG_TIMEOUT + startTime; - now = PR_Now(); - // PRTime is not guaranteed to be a signed integral type (afaik), but - // currently it is - if (now > expectedEndTime) { - waitTime = 0; - } else { - // This is a positive value, and we know that it will not overflow - // (bounded by WATCHDOG_TIMEOUT) - // waitTime is in milliseconds, nspr uses microseconds - waitTime = static_cast((expectedEndTime - now)/PR_USEC_PER_MSEC); - } - HANDLE hThread = waitHandles[2] = scan->GetWaitableThreadHandle(); - - // Wait for the thread (obj 1) or quit event (obj 0) - waitStatus = WaitForMultipleObjects(2, (waitHandles+1), FALSE, waitTime); - CloseHandle(hThread); - - ReleaseDispatcher* releaser = new ReleaseDispatcher(ToSupports(scan)); - if(!releaser) - continue; - NS_ADDREF(releaser); - // Got quit event or error - if (waitStatus == WAIT_FAILED || waitStatus == WAIT_OBJECT_0) { - NS_DispatchToMainThread(releaser); - break; - // Thread exited normally - } else if (waitStatus == (WAIT_OBJECT_0+1)) { - NS_DispatchToMainThread(releaser); - continue; - // Timeout case - } else { - NS_ASSERTION(waitStatus == WAIT_TIMEOUT, "Unexpected wait status in dlmgr watchdog thread"); - if (!scan->NotifyTimeout()) { - // If we didn't time out, then release the thread - NS_DispatchToMainThread(releaser); - } else { - // NotifyTimeout did a dispatch which will release the scan, so we - // don't need to release the scan - NS_RELEASE(releaser); - } - } - } - _endthreadex(0); - return 0; -} diff --git a/toolkit/components/downloads/nsDownloadScanner.h b/toolkit/components/downloads/nsDownloadScanner.h deleted file mode 100644 index 3301489fe6da0..0000000000000 --- a/toolkit/components/downloads/nsDownloadScanner.h +++ /dev/null @@ -1,121 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* 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/. */ - -/* vim: se cin sw=2 ts=2 et : */ - -#ifndef nsDownloadScanner_h_ -#define nsDownloadScanner_h_ - -#ifdef WIN32_LEAN_AND_MEAN -#undef WIN32_LEAN_AND_MEAN -#endif -#include -#define AVVENDOR -#include -#include -#include - -#include "nsAutoPtr.h" -#include "nsThreadUtils.h" -#include "nsTArray.h" -#include "nsIObserver.h" -#include "nsIURI.h" - -enum AVScanState -{ - AVSCAN_NOTSTARTED = 0, - AVSCAN_SCANNING, - AVSCAN_GOOD, - AVSCAN_BAD, - AVSCAN_UGLY, - AVSCAN_FAILED, - AVSCAN_TIMEDOUT -}; - -enum AVCheckPolicyState -{ - AVPOLICY_DOWNLOAD, - AVPOLICY_PROMPT, - AVPOLICY_BLOCKED -}; - -// See nsDownloadScanner.cpp for declaration and definition -class nsDownloadScannerWatchdog; -class nsDownload; - -class nsDownloadScanner -{ -public: - nsDownloadScanner(); - ~nsDownloadScanner(); - nsresult Init(); - nsresult ScanDownload(nsDownload *download); - AVCheckPolicyState CheckPolicy(nsIURI *aSource, nsIURI *aTarget); - -private: - bool mAESExists; - nsTArray mScanCLSID; - bool IsAESAvailable(); - bool EnumerateOAVProviders(); - - nsAutoPtr mWatchdog; - - static unsigned int __stdcall ScannerThreadFunction(void *p); - class Scan : public mozilla::Runnable - { - public: - Scan(nsDownloadScanner *scanner, nsDownload *download); - ~Scan(); - nsresult Start(); - - // Returns the time that Start was called - PRTime GetStartTime() const { return mStartTime; } - // Returns a copy of the thread handle that can be waited on, but not - // terminated - // The caller is responsible for closing the handle - // If the thread has terminated, then this will return the pseudo-handle - // INVALID_HANDLE_VALUE - HANDLE GetWaitableThreadHandle() const; - - // Called on a secondary thread to notify the scan that it has timed out - // this is used only by the watchdog thread - bool NotifyTimeout(); - - private: - nsDownloadScanner *mDLScanner; - PRTime mStartTime; - HANDLE mThread; - RefPtr mDownload; - // Guards mStatus - CRITICAL_SECTION mStateSync; - AVScanState mStatus; - nsString mPath; - nsString mName; - nsString mOrigin; - // Also true if it is an ftp download - bool mIsHttpDownload; - bool mSkipSource; - - /* @summary Sets the Scan's state to newState if the current state is - expectedState - * @param newState The new state of the scan - * @param expectedState The state that the caller expects the scan to be in - * @return If the old state matched expectedState - */ - bool CheckAndSetState(AVScanState newState, AVScanState expectedState); - - NS_IMETHOD Run(); - - void DoScan(); - bool DoScanAES(); - bool DoScanOAV(); - - friend unsigned int __stdcall nsDownloadScanner::ScannerThreadFunction(void *); - }; - // Used to give access to Scan - friend class nsDownloadScannerWatchdog; -}; -#endif - diff --git a/toolkit/components/typeaheadfind/nsTypeAheadFind.h b/toolkit/components/typeaheadfind/nsTypeAheadFind.h index 8ff5ad1bfe1e3..9416cbfc98d45 100644 --- a/toolkit/components/typeaheadfind/nsTypeAheadFind.h +++ b/toolkit/components/typeaheadfind/nsTypeAheadFind.h @@ -3,6 +3,7 @@ * 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 "nsComponentManagerUtils.h" #include "nsCycleCollectionParticipant.h" #include "nsISelectionController.h" #include "nsIController.h" From f3d8ecd61187b8c0b61d1f59b45300eec6a5388b Mon Sep 17 00:00:00 2001 From: Paolo Amadini Date: Thu, 11 May 2017 09:37:10 +0100 Subject: [PATCH 31/37] Bug 1364050 - Part 4 - Remove references to "downloads.rdf". r=mak MozReview-Commit-ID: y3BfYQYkF2 --HG-- extra : rebase_source : 0f09e7961aa0f226ba4cc1616172ee4856a50ee8 --- toolkit/xre/nsXREDirProvider.cpp | 3 --- xpcom/io/nsAppDirectoryServiceDefs.h | 2 -- 2 files changed, 5 deletions(-) diff --git a/toolkit/xre/nsXREDirProvider.cpp b/toolkit/xre/nsXREDirProvider.cpp index 1819e06cd0095..3759423859a3e 100644 --- a/toolkit/xre/nsXREDirProvider.cpp +++ b/toolkit/xre/nsXREDirProvider.cpp @@ -530,9 +530,6 @@ nsXREDirProvider::GetFile(const char* aProperty, bool* aPersistent, ensureFilePermissions = true; } } - else if (!strcmp(aProperty, NS_APP_DOWNLOADS_50_FILE)) { - rv = file->AppendNative(NS_LITERAL_CSTRING("downloads.rdf")); - } else if (!strcmp(aProperty, NS_APP_PREFS_OVERRIDE_DIR)) { rv = mProfileDir->Clone(getter_AddRefs(file)); nsresult tmp = file->AppendNative(NS_LITERAL_CSTRING(PREF_OVERRIDE_DIRNAME)); diff --git a/xpcom/io/nsAppDirectoryServiceDefs.h b/xpcom/io/nsAppDirectoryServiceDefs.h index 90449aebbe29f..b0284ea9ec04d 100644 --- a/xpcom/io/nsAppDirectoryServiceDefs.h +++ b/xpcom/io/nsAppDirectoryServiceDefs.h @@ -73,8 +73,6 @@ #define NS_APP_USER_PANELS_50_FILE "UPnls" #define NS_APP_CACHE_PARENT_DIR "cachePDir" -#define NS_APP_DOWNLOADS_50_FILE "DLoads" - #define NS_APP_SEARCH_50_FILE "SrchF" #define NS_APP_INSTALL_CLEANUP_DIR "XPIClnupD" //location of xpicleanup.dat xpicleanup.exe From 39e03eb845e76832bae27b5c4e3304c183e10f6b Mon Sep 17 00:00:00 2001 From: Paolo Amadini Date: Fri, 5 May 2017 13:11:49 +0100 Subject: [PATCH 32/37] Bug 1364050 - Part 5 - Update obsolete references in code comments. r=mak MozReview-Commit-ID: 9EUn7vcSfe3 --HG-- extra : rebase_source : f23b5f67b42d19fc69b06e3408d066ae479d369b --- browser/base/content/urlbarBindings.xml | 2 +- browser/components/downloads/DownloadsCommon.jsm | 12 +++--------- .../components/preferences/in-content-old/main.js | 9 --------- .../preferences/in-content/applications.js | 9 --------- toolkit/components/places/SQLFunctions.cpp | 2 -- uriloader/exthandler/nsExternalHelperAppService.cpp | 4 ++-- 6 files changed, 6 insertions(+), 32 deletions(-) diff --git a/browser/base/content/urlbarBindings.xml b/browser/base/content/urlbarBindings.xml index 9238c5a0032f9..9c288e1e7273c 100644 --- a/browser/base/content/urlbarBindings.xml +++ b/browser/base/content/urlbarBindings.xml @@ -2077,7 +2077,7 @@ file, You can obtain one at http://mozilla.org/MPL/2.0/. delta /= 1000; - // This code is taken from nsDownloadManager.cpp + // This algorithm is the same used by the downloads code. let speed = (aProgress - this.notification.lastProgress) / delta; if (this.notification.speed) speed = speed * 0.9 + this.notification.speed * 0.1; diff --git a/browser/components/downloads/DownloadsCommon.jsm b/browser/components/downloads/DownloadsCommon.jsm index 1f648f8cf100b..0556914f3e24d 100644 --- a/browser/components/downloads/DownloadsCommon.jsm +++ b/browser/components/downloads/DownloadsCommon.jsm @@ -634,17 +634,11 @@ XPCOMUtils.defineLazyGetter(DownloadsCommon, "isWinVistaOrHigher", function() { /** * Retrieves the list of past and completed downloads from the underlying - * Download Manager data, and provides asynchronous notifications allowing to + * Downloads API data, and provides asynchronous notifications allowing to * build a consistent view of the available data. * - * This object responds to real-time changes in the underlying Download Manager - * data. For example, the deletion of one or more downloads is notified through - * the nsIObserver interface, while any state or progress change is notified - * through the nsIDownloadProgressListener interface. - * - * Note that using this object does not automatically start the Download Manager - * service. Consumers will see an empty list of downloads until the service is - * actually started. This is useful to display a neutral progress indicator in + * Note that using this object does not automatically initialize the list of + * downloads. This is useful to display a neutral progress indicator in * the main browser window until the autostart timeout elapses. * * Note that DownloadsData and PrivateDownloadsData are two equivalent singleton diff --git a/browser/components/preferences/in-content-old/main.js b/browser/components/preferences/in-content-old/main.js index 86aac95ae03b1..b0948e49b3033 100644 --- a/browser/components/preferences/in-content-old/main.js +++ b/browser/components/preferences/in-content-old/main.js @@ -507,15 +507,6 @@ var gMainPane = { iconUrlSpec = fph.getURLSpecFromFile(currentDirPref.value); } else if (folderListPref.value == 1) { // 'Downloads' - // In 1.5, this pointed to a folder we created called 'My Downloads' - // and was available as an option in the 1.5 drop down. On XP this - // was in My Documents, on OSX it was in User Docs. In 2.0, we did - // away with the drop down option, although the special label was - // still supported for the folder if it existed. Because it was - // not exposed it was rarely used. - // With 3.0, a new desktop folder - 'Downloads' was introduced for - // platforms and versions that don't support a default system downloads - // folder. See nsDownloadManager for details. downloadFolder.label = bundlePreferences.getString("downloadsFolderName"); iconUrlSpec = fph.getURLSpecFromFile(await this._indexToFolder(1)); } else { diff --git a/browser/components/preferences/in-content/applications.js b/browser/components/preferences/in-content/applications.js index e09109f3a70c1..33b10e1012a01 100644 --- a/browser/components/preferences/in-content/applications.js +++ b/browser/components/preferences/in-content/applications.js @@ -2019,15 +2019,6 @@ var gApplicationsPane = { iconUrlSpec = fph.getURLSpecFromFile(currentDirPref.value); } else if (folderListPref.value == 1) { // 'Downloads' - // In 1.5, this pointed to a folder we created called 'My Downloads' - // and was available as an option in the 1.5 drop down. On XP this - // was in My Documents, on OSX it was in User Docs. In 2.0, we did - // away with the drop down option, although the special label was - // still supported for the folder if it existed. Because it was - // not exposed it was rarely used. - // With 3.0, a new desktop folder - 'Downloads' was introduced for - // platforms and versions that don't support a default system downloads - // folder. See nsDownloadManager for details. downloadFolder.label = bundlePreferences.getString("downloadsFolderName"); iconUrlSpec = fph.getURLSpecFromFile(await this._indexToFolder(1)); } else { diff --git a/toolkit/components/places/SQLFunctions.cpp b/toolkit/components/places/SQLFunctions.cpp index da12939c74476..f69cf06ebfc86 100644 --- a/toolkit/components/places/SQLFunctions.cpp +++ b/toolkit/components/places/SQLFunctions.cpp @@ -25,8 +25,6 @@ using namespace mozilla::storage; -// Keep the GUID-related parts of this file in sync with toolkit/downloads/SQLFunctions.cpp! - //////////////////////////////////////////////////////////////////////////////// //// Anonymous Helpers diff --git a/uriloader/exthandler/nsExternalHelperAppService.cpp b/uriloader/exthandler/nsExternalHelperAppService.cpp index 992656edcca61..d4e15ae72ff79 100644 --- a/uriloader/exthandler/nsExternalHelperAppService.cpp +++ b/uriloader/exthandler/nsExternalHelperAppService.cpp @@ -273,7 +273,7 @@ static bool GetFilenameAndExtensionFromChannel(nsIChannel* aChannel, * Obtains the directory to use. This tends to vary per platform, and * needs to be consistent throughout our codepaths. For platforms where * helper apps use the downloads directory, this should be kept in - * sync with nsDownloadManager.cpp + * sync with DownloadIntegration.jsm. * * Optionally skip availability of the directory and storage. */ @@ -2427,7 +2427,7 @@ NS_IMETHODIMP nsExternalAppHandler::LaunchWithApplication(nsIFile * aApplication // was specified in mSuggestedFileName after the download is done prior to // launching the helper app. So that any existing file of that name won't be // overwritten we call CreateUnique(). Also note that we use the same - // directory as originally downloaded so nsDownload can rename in place + // directory as originally downloaded so the download can be renamed in place // later. nsCOMPtr fileToUse; (void) GetDownloadDirectory(getter_AddRefs(fileToUse)); From 60cfc710b23decfd0f5d4ec3e353688310cf2e19 Mon Sep 17 00:00:00 2001 From: "L. David Baron" Date: Wed, 17 May 2017 11:59:56 -0700 Subject: [PATCH 33/37] Bug 1365443 - Unbreak wildcard rules in GECKO_DISPLAY_REFLOW_RULES_FILE. r=emilio This restores the code to what it was doing prior to bug 1360241 (where the else to add to mWildRules was an else of a test of fType), but then removes the unnecesary null-check on the result of GetFrameTypeInfo. MozReview-Commit-ID: EOPumJoKrIs --HG-- extra : transplant_source : %E4%C8%A7%21%98%FAx%C7%1E%A5%0D%B5-%C5%BA%80%D4%1C%A8M --- layout/generic/nsFrame.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/layout/generic/nsFrame.cpp b/layout/generic/nsFrame.cpp index afa1ee44f997b..9d14e9f19e06a 100644 --- a/layout/generic/nsFrame.cpp +++ b/layout/generic/nsFrame.cpp @@ -11072,8 +11072,8 @@ void DR_State::ParseRulesFile() for (DR_Rule* rule = ParseRule(inFile); rule; rule = ParseRule(inFile)) { if (rule->mTarget) { LayoutFrameType fType = rule->mTarget->mFrameType; - DR_FrameTypeInfo* info = GetFrameTypeInfo(fType); - if (info) { + if (fType != LayoutFrameType::None) { + DR_FrameTypeInfo* info = GetFrameTypeInfo(fType); AddRule(info->mRules, *rule); } else { From ce4248324c726c921a2c79bfeb5ca87c2eec9dde Mon Sep 17 00:00:00 2001 From: David Major Date: Wed, 17 May 2017 15:26:13 -0400 Subject: [PATCH 34/37] Bug 1365415: No need to hook LdrResolveDelayLoadedAPI on Win7, it doesn't exist. r=mstange --HG-- extra : rebase_source : 3ba5eee05ca2bf7e50e6c8fb1fd29a8e4ab32686 --- mozglue/misc/StackWalk.cpp | 9 ++++++--- toolkit/xre/test/win/TestDllInterceptor.cpp | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/mozglue/misc/StackWalk.cpp b/mozglue/misc/StackWalk.cpp index d9e9188e09493..09b184731e681 100644 --- a/mozglue/misc/StackWalk.cpp +++ b/mozglue/misc/StackWalk.cpp @@ -190,6 +190,7 @@ StackWalkInitCriticalAddress() #include "mozilla/ArrayUtils.h" #include "mozilla/Atomics.h" #include "mozilla/StackWalk_windows.h" +#include "mozilla/WindowsVersion.h" #ifdef MOZ_STATIC_JS // The standalone SM build lacks the interceptor headers. #include "nsWindowsDllInterceptor.h" @@ -406,9 +407,11 @@ EnsureWalkThreadReady() NtDllInterceptor.AddHook("LdrUnloadDll", reinterpret_cast(patched_LdrUnloadDll), (void**)&stub_LdrUnloadDll); - NtDllInterceptor.AddHook("LdrResolveDelayLoadedAPI", - reinterpret_cast(patched_LdrResolveDelayLoadedAPI), - (void**)&stub_LdrResolveDelayLoadedAPI); + if (IsWin8OrLater()) { // LdrResolveDelayLoadedAPI was introduced in Win8 + NtDllInterceptor.AddHook("LdrResolveDelayLoadedAPI", + reinterpret_cast(patched_LdrResolveDelayLoadedAPI), + (void**)&stub_LdrResolveDelayLoadedAPI); + } #endif InitializeDbgHelpCriticalSection(); diff --git a/toolkit/xre/test/win/TestDllInterceptor.cpp b/toolkit/xre/test/win/TestDllInterceptor.cpp index 51fb4191bf825..4461e24f7008a 100644 --- a/toolkit/xre/test/win/TestDllInterceptor.cpp +++ b/toolkit/xre/test/win/TestDllInterceptor.cpp @@ -483,7 +483,7 @@ int main() #ifdef _M_X64 TestHook(TestGetKeyState, "user32.dll", "GetKeyState") && // see Bug 1316415 TestHook(TestLdrUnloadDll, "ntdll.dll", "LdrUnloadDll") && - TestHook(TestLdrResolveDelayLoadedAPI, "ntdll.dll", "LdrResolveDelayLoadedAPI") && + MaybeTestHook(IsWin8OrLater(), TestLdrResolveDelayLoadedAPI, "ntdll.dll", "LdrResolveDelayLoadedAPI") && #endif MaybeTestHook(ShouldTestTipTsf(), TestProcessCaretEvents, "tiptsf.dll", "ProcessCaretEvents") && #ifdef _M_IX86 From 8adfa773bb383cdf8c3dcf272d70799db8726ccc Mon Sep 17 00:00:00 2001 From: Bill McCloskey Date: Wed, 17 May 2017 12:58:35 -0700 Subject: [PATCH 35/37] Revert "Bug 1363877 - Label IPC shmem messages as SystemGroup (r=dvander)" for valgrind failures This reverts commit 1f7a7e259501b47a014959049e2ed069fe2fd9b8. --- ipc/glue/ProtocolUtils.cpp | 10 ---------- ipc/glue/ProtocolUtils.h | 4 ---- xpcom/threads/SystemGroup.cpp | 8 -------- xpcom/threads/SystemGroup.h | 3 --- 4 files changed, 25 deletions(-) diff --git a/ipc/glue/ProtocolUtils.cpp b/ipc/glue/ProtocolUtils.cpp index aec0ddeb2749d..1d6559bf9201e 100644 --- a/ipc/glue/ProtocolUtils.cpp +++ b/ipc/glue/ProtocolUtils.cpp @@ -19,7 +19,6 @@ #include "mozilla/ipc/MessageChannel.h" #include "mozilla/ipc/Transport.h" #include "mozilla/StaticMutex.h" -#include "mozilla/SystemGroup.h" #include "mozilla/Unused.h" #include "nsPrintfCString.h" @@ -633,7 +632,6 @@ IToplevelProtocol::Open(mozilla::ipc::Transport* aTransport, MessageLoop* aThread, mozilla::ipc::Side aSide) { - mIsMainThreadProtocol = NS_IsMainThread(); SetOtherProcessId(aOtherPid); return GetIPCChannel()->Open(aTransport, aThread, aSide); } @@ -643,7 +641,6 @@ IToplevelProtocol::Open(MessageChannel* aChannel, MessageLoop* aMessageLoop, mozilla::ipc::Side aSide) { - mIsMainThreadProtocol = NS_IsMainThread(); SetOtherProcessId(base::GetCurrentProcId()); return GetIPCChannel()->Open(aChannel, aMessageLoop, aSide); } @@ -818,13 +815,6 @@ IToplevelProtocol::ShmemDestroyed(const Message& aMsg) already_AddRefed IToplevelProtocol::GetMessageEventTarget(const Message& aMsg) { - if (IsMainThreadProtocol() && SystemGroup::Initialized()) { - if (aMsg.type() == SHMEM_CREATED_MESSAGE_TYPE || - aMsg.type() == SHMEM_DESTROYED_MESSAGE_TYPE) { - return do_AddRef(SystemGroup::EventTargetFor(TaskCategory::Other)); - } - } - int32_t route = aMsg.routing_id(); Maybe lock; diff --git a/ipc/glue/ProtocolUtils.h b/ipc/glue/ProtocolUtils.h index 69ea6763f8321..8f02d85c81791 100644 --- a/ipc/glue/ProtocolUtils.h +++ b/ipc/glue/ProtocolUtils.h @@ -386,9 +386,6 @@ class IToplevelProtocol : public IProtocol GetActorEventTarget(); virtual void OnChannelReceivedMessage(const Message& aMsg) {} - - bool IsMainThreadProtocol() const { return mIsMainThreadProtocol; } - protected: // Override this method in top-level protocols to change the event target // for a new actor (and its sub-actors). @@ -416,7 +413,6 @@ class IToplevelProtocol : public IProtocol int32_t mLastRouteId; IDMap mShmemMap; Shmem::id_t mLastShmemId; - bool mIsMainThreadProtocol; Mutex mEventTargetMutex; IDMap> mEventTargetMap; diff --git a/xpcom/threads/SystemGroup.cpp b/xpcom/threads/SystemGroup.cpp index 5973907ed8921..a8ba2828a4484 100644 --- a/xpcom/threads/SystemGroup.cpp +++ b/xpcom/threads/SystemGroup.cpp @@ -22,8 +22,6 @@ class SystemGroupImpl final : public SchedulerGroup static void ShutdownStatic(); static SystemGroupImpl* Get(); - static bool Initialized() { return !!sSingleton; } - NS_METHOD_(MozExternalRefCountType) AddRef(void) { return 2; @@ -78,12 +76,6 @@ SystemGroup::Shutdown() SystemGroupImpl::ShutdownStatic(); } -bool -SystemGroup::Initialized() -{ - return SystemGroupImpl::Initialized(); -} - /* static */ nsresult SystemGroup::Dispatch(const char* aName, TaskCategory aCategory, diff --git a/xpcom/threads/SystemGroup.h b/xpcom/threads/SystemGroup.h index 1be37291f56b3..a8b70383164cc 100644 --- a/xpcom/threads/SystemGroup.h +++ b/xpcom/threads/SystemGroup.h @@ -34,9 +34,6 @@ class SystemGroup static void InitStatic(); static void Shutdown(); - - // Returns true if SystemGroup has been initialized. - static bool Initialized(); }; } // namespace mozilla From cdcfb4139e6b3cabbb5cede105a74312cf0dff13 Mon Sep 17 00:00:00 2001 From: Kris Maglione Date: Wed, 17 May 2017 12:55:42 -0700 Subject: [PATCH 36/37] Bug 1365706: Use a special set of startup data when running in safe mode. r=rhelmer MozReview-Commit-ID: DcRnYu3rNot --HG-- extra : rebase_source : d72ad915b93733cb8f7e19cca4312ffbc3fd7660 --- .../extensions/internal/XPIProvider.jsm | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/toolkit/mozapps/extensions/internal/XPIProvider.jsm b/toolkit/mozapps/extensions/internal/XPIProvider.jsm index 34f0320b57559..632f83248c8a4 100644 --- a/toolkit/mozapps/extensions/internal/XPIProvider.jsm +++ b/toolkit/mozapps/extensions/internal/XPIProvider.jsm @@ -252,6 +252,8 @@ const FILE_RDF_MANIFEST = "install.rdf"; const FILE_WEB_MANIFEST = "manifest.json"; const FILE_XPI_ADDONS_LIST = "extensions.ini"; +const ADDON_ID_DEFAULT_THEME = "{972ce4c6-7e08-4474-a285-3208198ce6fd}"; + const KEY_PROFILEDIR = "ProfD"; const KEY_ADDON_APP_DIR = "XREAddonAppDir"; const KEY_TEMPDIR = "TmpD"; @@ -4079,6 +4081,30 @@ this.XPIProvider = { .filter(addon => addon.dependencies.includes(aAddon.id)); }, + /** + * Returns the add-on state data for the restartful extensions which + * should be available in safe mode. In particular, this means the + * default theme, and only the default theme. + * + * @returns {object} + */ + getSafeModeExtensions() { + let loc = XPIStates.getLocation(KEY_APP_GLOBAL); + let state = loc.get(ADDON_ID_DEFAULT_THEME); + + // Use the default state data for the default theme, but always mark + // it enabled, in case another theme is enabled in normal mode. + let addonData = state.toJSON(); + addonData.enabled = true; + + return { + [KEY_APP_GLOBAL]: { + path: loc.path, + addons: { [ADDON_ID_DEFAULT_THEME]: addonData }, + }, + }; + }, + /** * Checks for any changes that have occurred since the last time the * application was launched. @@ -4198,6 +4224,12 @@ this.XPIProvider = { } } + if (Services.appinfo.inSafeMode) { + aomStartup.initializeExtensions(this.getSafeModeExtensions()); + logger.debug("Initialized safe mode add-ons"); + return false; + } + // If the application crashed before completing any pending operations then // we should perform them now. if (extensionListChanged || hasPendingChanges) { From 6be80897312a3a95bd18f46d0b2fc2091fa42ff5 Mon Sep 17 00:00:00 2001 From: Mike Hommey Date: Thu, 18 May 2017 08:13:24 +0900 Subject: [PATCH 37/37] Bug 1365788 - Always define NOMINMAX in spidermonkey. r=sfink. a=backout --HG-- extra : rebase_source : 1c31350716806945c896f62077cf74d3cf31c79e extra : amend_source : 4e8a0407c52e638ee6fc2a59b5afdde30e6dcf21 --- js/src/jswin.h | 2 -- js/src/moz.build | 3 --- js/src/old-configure.in | 2 ++ 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/js/src/jswin.h b/js/src/jswin.h index ad95b1332b9f4..a14524ee18fc9 100644 --- a/js/src/jswin.h +++ b/js/src/jswin.h @@ -15,8 +15,6 @@ #ifdef XP_WIN # include # undef assert -# undef min -# undef max # undef GetProp # undef MemoryBarrier # undef SetProp diff --git a/js/src/moz.build b/js/src/moz.build index 2132902833ca9..ceb8877c4ba39 100644 --- a/js/src/moz.build +++ b/js/src/moz.build @@ -737,9 +737,6 @@ if CONFIG['GNU_CXX']: if CONFIG['CLANG_CXX']: SOURCES['jsdtoa.cpp'].flags += ['-Wno-implicit-fallthrough'] -if CONFIG['OS_ARCH'] == 'WINNT': - DEFINES['NOMINMAX'] = True - # Generate GC statistics phase data. GENERATED_FILES += ['gc/StatsPhasesGenerated.h'] StatsPhasesGeneratedHeader = GENERATED_FILES['gc/StatsPhasesGenerated.h'] diff --git a/js/src/old-configure.in b/js/src/old-configure.in index a73dd2ec16831..1ff24a5408f47 100644 --- a/js/src/old-configure.in +++ b/js/src/old-configure.in @@ -811,6 +811,8 @@ case "$target" in fi AC_DEFINE(HAVE__MSIZE) AC_DEFINE(WIN32_LEAN_AND_MEAN) + dnl See http://support.microsoft.com/kb/143208 to use STL + AC_DEFINE(NOMINMAX) BIN_SUFFIX='.exe' MOZ_USER_DIR="Mozilla"