From 47369d521f401de4355d77cf0e6140fa7b7f334d Mon Sep 17 00:00:00 2001 From: Joshua Cranmer Date: Fri, 20 Feb 2015 20:48:29 -0600 Subject: [PATCH 01/48] Bug 1054308 - Add mozmill tests to the mozharness configurations for Thunderbird, r=ted --- testing/config/mozharness/linux_config.py | 10 +++++++++- testing/config/mozharness/mac_config.py | 8 ++++++++ testing/config/mozharness/windows_config.py | 10 +++++++++- 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/testing/config/mozharness/linux_config.py b/testing/config/mozharness/linux_config.py index 043b8fc632f..7ebc4cab6ed 100644 --- a/testing/config/mozharness/linux_config.py +++ b/testing/config/mozharness/linux_config.py @@ -49,6 +49,14 @@ config = { "run_filename": "test.py", "testsdir": "mozbase" }, + "mozmill": { + "options": [ + "--binary=%(binary_path)s", + "--symbols-path=%(symbols_path)s" + ], + "run_filename": "runtestlist.py", + "testsdir": "mozmill" + }, "reftest": { "options": [ "--appname=%(binary_path)s", @@ -85,4 +93,4 @@ config = { "testsdir": "xpcshell" } } -} \ No newline at end of file +} diff --git a/testing/config/mozharness/mac_config.py b/testing/config/mozharness/mac_config.py index dee504b0331..6d1b2cbdea0 100644 --- a/testing/config/mozharness/mac_config.py +++ b/testing/config/mozharness/mac_config.py @@ -47,6 +47,14 @@ config = { "run_filename": "test.py", "testsdir": "mozbase" }, + "mozmill": { + "options": [ + "--binary=%(binary_path)s", + "--symbols-path=%(symbols_path)s" + ], + "run_filename": "runtestlist.py", + "testsdir": "mozmill" + }, "reftest": { "options": [ "--appname=%(binary_path)s", diff --git a/testing/config/mozharness/windows_config.py b/testing/config/mozharness/windows_config.py index 66e95f42209..6d1b2cbdea0 100644 --- a/testing/config/mozharness/windows_config.py +++ b/testing/config/mozharness/windows_config.py @@ -47,6 +47,14 @@ config = { "run_filename": "test.py", "testsdir": "mozbase" }, + "mozmill": { + "options": [ + "--binary=%(binary_path)s", + "--symbols-path=%(symbols_path)s" + ], + "run_filename": "runtestlist.py", + "testsdir": "mozmill" + }, "reftest": { "options": [ "--appname=%(binary_path)s", @@ -83,4 +91,4 @@ config = { "testsdir": "xpcshell" } } -} \ No newline at end of file +} From db369e38e7bd0664cd469a396240f8924fb2a4e1 Mon Sep 17 00:00:00 2001 From: Joe Steele Date: Sat, 21 Feb 2015 16:43:36 +1300 Subject: [PATCH 02/48] Bug 1131798 - Fix handling of CPU sub-type and rebasing WITHOUT requiring Python 3.3. r=ted --- python/eme/gen-eme-voucher.py | 346 ++++++++++++++++++++++++++-------- 1 file changed, 268 insertions(+), 78 deletions(-) diff --git a/python/eme/gen-eme-voucher.py b/python/eme/gen-eme-voucher.py index 24d1cb8b98d..40da5d11308 100644 --- a/python/eme/gen-eme-voucher.py +++ b/python/eme/gen-eme-voucher.py @@ -1,4 +1,5 @@ -#!/usr/bin/env python +#!/usr/bin/env python2.7 +# # Copyright 2014 Adobe Systems Incorporated. All Rights Reserved. # # Adobe permits you to use, modify, and distribute this file in accordance @@ -6,17 +7,18 @@ # a copy of the MPL was not distributed with this file, You can obtain one # at http://mozilla.org/MPL/2.0/. # -# Create perforce changelist of modules from FTP server +# Creates an Adobe Access signed voucher for any executable +# Notes: This is currently python2.7 due to mozilla build system requirements -import io, argparse,pyasn1, bitstring -from pyasn1.codec.der import encoder, decoder +import argparse, bitstring, pprint, hashlib, os, subprocess, sys, tempfile +from pyasn1.codec.der import encoder as der_encoder from pyasn1.type import univ, namedtype, namedval, constraint -import hashlib + # CodeSectionDigest ::= SEQUENCE { -# offset INTEGER -- section's file offset in the signed binary -# digestAlgorithm OBJECT IDENTIFIER -- algorithm identifier for the hash value below. For now only supports SHA256. -# digestValue OCTET STRING -- hash value of the TEXT segment. +# offset INTEGER -- section's file offset in the signed binary +# digestAlgorithm OBJECT IDENTIFIER -- algorithm identifier for the hash value below. For now only supports SHA256. +# digestValue OCTET STRING -- hash value of the TEXT segment. # } class CodeSectionDigest(univ.Sequence): componentType = namedtype.NamedTypes( @@ -24,6 +26,7 @@ class CodeSectionDigest(univ.Sequence): namedtype.NamedType('digestAlgorithm', univ.ObjectIdentifier()), namedtype.NamedType('digest', univ.OctetString())) + # CodeSegmentDigest ::= SEQUENCE { # offset INTEGER -- TEXT segment's file offset in the signed binary # codeSectionDigests SET OF CodeSectionDigests @@ -32,20 +35,22 @@ class CodeSectionDigest(univ.Sequence): class SetOfCodeSectionDigest(univ.SetOf): componentType = CodeSectionDigest() + class CodeSegmentDigest(univ.Sequence): componentType = namedtype.NamedTypes( namedtype.NamedType('offset', univ.Integer()), namedtype.NamedType('codeSectionDigests', SetOfCodeSectionDigest())) + # ArchitectureDigest ::= SEQUENCE { # cpuType ENUMERATED CpuType # cpuSubType ENUMERATED CpuSubType # CodeSegmentDigests SET OF CodeSegmentDigests # } - class SetOfCodeSegmentDigest(univ.SetOf): componentType = CodeSegmentDigest() + class CPUType(univ.Enumerated): namedValues = namedval.NamedValues( ('IMAGE_FILE_MACHINE_I386', 0x14c), @@ -55,34 +60,46 @@ class CPUType(univ.Enumerated): constraint.SingleValueConstraint(0x14c, 0x8664) +class CPUSubType(univ.Enumerated): + namedValues = namedval.NamedValues( + ('IMAGE_UNUSED', 0x0), + ) + subtypeSpec = univ.Enumerated.subtypeSpec + \ + constraint.SingleValueConstraint(0) + + class ArchitectureDigest(univ.Sequence): componentType = namedtype.NamedTypes( namedtype.NamedType('cpuType', CPUType()), - namedtype.NamedType('cpuSubType', univ.Integer()), + namedtype.NamedType('cpuSubType', CPUSubType()), namedtype.NamedType('CodeSegmentDigests', SetOfCodeSegmentDigest()) ) -# ApplicationDigest ::= SEQUENCE { -# version INTEGER -# digests SET OF ArchitectureDigest -# } + +# ApplicationDigest ::= SEQUENCE { +# version INTEGER +# digests SET OF ArchitectureDigest +# } class SetOfArchitectureDigest(univ.SetOf): componentType = ArchitectureDigest() + class ApplicationDigest(univ.Sequence): componentType = namedtype.NamedTypes( namedtype.NamedType('version', univ.Integer()), namedtype.NamedType('digests', SetOfArchitectureDigest()) ) -def meetsRequirements(items, requirements): + +def meets_requirements(items, requirements): for r in requirements: for n, v in r.items(): if n not in items or items[n] != v: return False return True + # return total number of bytes read from items_in excluding leaves -def parseItems(stream, items_in, items_out): +def parse_items(stream, items_in, items_out): bits_read = 0 total_bits_read = 0 @@ -102,7 +119,7 @@ def parseItems(stream, items_in, items_out): requirements = list(filter(lambda x: isinstance(x, dict), item[2])) sub_items = list(filter(lambda x: isinstance(x, tuple), item[2])) - if not meetsRequirements(items_out, requirements): continue + if not meets_requirements(items_out, requirements): continue # has sub-items based on length items_out[name] = stream.read(t) @@ -113,7 +130,7 @@ def parseItems(stream, items_in, items_out): bit_length = items_out[name] * 8 if bit_length > 0: - sub_read, sub_total_read = parseItems(stream, sub_items, items_out) + sub_read, sub_total_read = parse_items(stream, sub_items, items_out) bit_length -= sub_read total_bits_read += sub_total_read @@ -127,6 +144,7 @@ def parseItems(stream, items_in, items_out): return bits_read, total_bits_read +# TODO: perhaps switch to pefile module when it officially supports python3 class SectionHeader: def __init__(self, stream): items = [ @@ -141,15 +159,115 @@ class SectionHeader: ('NumberOfLineNumbers', 'uintle:16'), ('Characteristics', 'uintle:32') ] - self.items = {} - _, self.bits_read = parseItems(stream, items, self.items) + self.items = dict() + self.relocs = dict() + + _, self.bits_read = parse_items(stream, items, self.items) self.sectionName = self.items['Name'].decode('utf-8') self.offset = self.items['PointerToRawData'] +COFF_DATA_DIRECTORY_TYPES = [ + "Export Table", + "Import Table", + "Resource Table", + "Exception Table", + "Certificate Tble", + "Base Relocation Table", + "Debug", + "Architecture", + "Global Ptr", + "TLS Table", + "Load Config Table", + "Bound Import", + "IAT", + "Delay Import Descriptor", + "CLR Runtime Header", + "Reserved", +] + + +def chained_safe_get(obj, names, default=None): + if obj is None: return default + + for n in names: + if n in obj: + obj = obj[n] + else: + return default + + return obj + + +class OptionalHeader: + def __init__(self, stream, size): + self.items = {} + items = [] + + if size: + items += [ + ('Magic', 'uintle:16'), + ('MajorLinkerVersion', 'uintle:8'), + ('MinorLinkerVersion', 'uintle:8'), + ('SizeOfCode', 'uintle:32'), + ('SizeOfInitializedData', 'uintle:32'), + ('SizeOfUninitializedData', 'uintle:32'), + ('AddressOfEntryPoint', 'uintle:32'), + ('BaseOfCode', 'uintle:32'), + ] + + _, self.bits_read = parse_items(stream, items, self.items) + + items = [] + if self.items['Magic'] == 0x10b: # PE32 + items += [('BaseOfData', 'uintle:32')] + + address_size = 'uintle:64' if self.items['Magic'] == 0x20b else 'uintle:32' + + items += [ + ('ImageBase', address_size), + ('SectionAlignment', 'uintle:32'), + ('FileAlignment', 'uintle:32'), + ('MajorOperatingSystemVersion', 'uintle:16'), + ('MinorOperatingSystemVersion', 'uintle:16'), + ('MajorImageVersion', 'uintle:16'), + ('MinorImageVersion', 'uintle:16'), + ('MajorSubsystemVersion', 'uintle:16'), + ('MinorSubsystemVersion', 'uintle:16'), + ('Win32VersionValue', 'uintle:32'), + ('SizeOfImage', 'uintle:32'), + ('SizeOfHeaders', 'uintle:32'), + ('CheckSum', 'uintle:32'), + ('Subsystem', 'uintle:16'), + ('DllCharacteristics', 'uintle:16'), + ('SizeOfStackReserve', address_size), + ('SizeOfStackCommit', address_size), + ('SizeOfHeapReserve', address_size), + ('SizeOfHeapCommit', address_size), + ('LoaderFlags', 'uintle:32'), + ('NumberOfRvaAndSizes', 'uintle:32'), + ] + + if size > 28: + _, bits_read = parse_items(stream, items, self.items) + self.bits_read += bits_read + + if 'NumberOfRvaAndSizes' in self.items: + index = 0 + self.items['Data Directories'] = dict() + while self.bits_read / 8 < size: + d = self.items['Data Directories'][COFF_DATA_DIRECTORY_TYPES[index]] = dict() + + _, bits_read = parse_items(stream, [('VirtualAddress', 'uintle:32'), ('Size', 'uintle:32')], d) + self.bits_read += bits_read + index += 1 + class COFFFileHeader: def __init__(self, stream): + self.items = {} + self.section_headers = [] + items = [ ('Machine', 'uintle:16'), ('NumberOfSections', 'uintle:16'), @@ -159,106 +277,178 @@ class COFFFileHeader: ('SizeOfOptionalHeader', 'uintle:16'), ('Characteristics', 'uintle:16') ] - self.items = {} - _, self.bits_read = parseItems(stream, items, self.items) + _, self.bits_read = parse_items(stream, items, self.items) - #skip over optional header. - if self.items['SizeOfOptionalHeader'] > 0: - stream.read(self.items['SizeOfOptionalHeader'] * 8) - self.bits_read += self.items['SizeOfOptionalHeader'] * 8 + self.OptionalHeader = OptionalHeader(stream, self.items['SizeOfOptionalHeader']) + self.bits_read += self.OptionalHeader.bits_read - #start reading section headers - numberOfSections = self.items['NumberOfSections'] - self.codeSectionHeaders = [] + # start reading section headers + num_sections = self.items['NumberOfSections'] - while numberOfSections > 0 : - sectionHeader = SectionHeader(stream) - if (sectionHeader.items['Characteristics'] & 0x20000000) == 0x20000000: - self.codeSectionHeaders.append(sectionHeader) - numberOfSections -= 1 + while num_sections > 0 : + section_header = SectionHeader(stream) + self.bits_read += section_header.bits_read + self.section_headers.append(section_header) + num_sections -= 1 - self.codeSectionHeaders.sort(key=lambda header: header.offset) + self.section_headers.sort(key=lambda header: header.offset) + + # Read Relocations + self.process_relocs(stream) + + def process_relocs(self, stream): + reloc_table = chained_safe_get(self.OptionalHeader.items, ['Data Directories', 'Base Relocation Table']) + if reloc_table is None: return + + orig_pos = stream.bitpos + _, stream.bytepos = self.get_rva_section(reloc_table['VirtualAddress']) + end_pos = stream.bitpos + reloc_table['Size'] * 8 + + while stream.bitpos < end_pos: + page_rva = stream.read('uintle:32') + block_size = stream.read('uintle:32') + + for i in range(0, int((block_size - 8) / 2)): + data = stream.read('uintle:16') + typ = data >> 12 + offset = data & 0xFFF + + if offset == 0 and i > 0: continue + + assert(typ == 3) + + cur_pos = stream.bitpos + sh, value_bytepos = self.get_rva_section(page_rva + offset) + stream.bytepos = value_bytepos + value = stream.read('uintle:32') + + # remove BaseAddress + value -= self.OptionalHeader.items['ImageBase'] + + stream.overwrite(bitstring.BitArray(uint=value, length=4 * 8), pos=value_bytepos * 8) + stream.pos = cur_pos + + stream.bitpos = orig_pos + + def get_rva_section(self, rva): + for sh in self.section_headers: + if rva < sh.items['VirtualAddress'] or rva >= sh.items['VirtualAddress'] + sh.items['VirtualSize']: + continue + + file_pointer = rva - sh.items['VirtualAddress'] + sh.items['PointerToRawData'] + return sh, file_pointer + + raise Exception('Could not match RVA to section') +def create_temp_file(suffix=""): + fd, path = tempfile.mkstemp(suffix=suffix) + os.close(fd) + return path + +# TIPS: +# How to convert PFX to PEM: openssl pkcs12 -in build/certificates/testPKI/IV.pfx -out build/certificates/testPKI/IV.cert.pem def main(): - parser = argparse.ArgumentParser(description='PE/COFF Parser.') + parser = argparse.ArgumentParser(description='PE/COFF Signer') parser.add_argument('-input', required=True, help="File to parse.") parser.add_argument('-output', required=True, help="File to write to.") + parser.add_argument('-openssl_path',help="Path to OpenSSL to create signed voucher") + parser.add_argument('-signer_cert',help="Path to certificate to use to sign voucher. Must be PEM encoded.") parser.add_argument('-verbose', action='store_true', help="Verbose output.") app_args = parser.parse_args() - stream = bitstring.ConstBitStream(filename=app_args.input) + # to simplify relocation handling we use a mutable BitStream so we can remove + # the BaseAddress from each relocation + stream = bitstring.BitStream(filename=app_args.input) + # find the COFF header. # skip forward past the MSDOS stub header to 0x3c. - stream.bytepos = 0x3c # read 4 bytes, this is the file offset of the PE signature. - peSignatureOffset = stream.read('uintle:32') - stream.bytepos = peSignatureOffset + pe_sig_offset = stream.read('uintle:32') + stream.bytepos = pe_sig_offset - #read 4 bytes, make sure it's a PE signature. + # read 4 bytes, make sure it's a PE signature. signature = stream.read('uintle:32') - if signature != 0x00004550 : - return + if signature != 0x00004550: + raise Exception("Invalid File") + # after signature is the actual COFF file header. + coff_header = COFFFileHeader(stream) - archDigest = ArchitectureDigest() + arch_digest = ArchitectureDigest() + if coff_header.items['Machine'] == 0x14c: + arch_digest.setComponentByName('cpuType', CPUType('IMAGE_FILE_MACHINE_I386')) + elif coff_header.items['Machine'] == 0x8664: + arch_digest.setComponentByName('cpuType', CPUType('IMAGE_FILE_MACHINE_AMD64')) - codeSegmentDigests = SetOfCodeSegmentDigest() - codeSegmentIdx = 0 + arch_digest.setComponentByName('cpuSubType', CPUSubType('IMAGE_UNUSED')) - #after signature is the actual COFF file header. - coffFileHeader = COFFFileHeader(stream) + text_section_headers = list(filter(lambda x: (x.items['Characteristics'] & 0x20000000) == 0x20000000, coff_header.section_headers)) - if coffFileHeader.items['Machine'] == 0x14c: - archDigest.setComponentByName('cpuType', CPUType('IMAGE_FILE_MACHINE_I386')) - elif coffFileHeader.items['Machine'] == 0x8664: - archDigest.setComponentByName('cpuType', CPUType('IMAGE_FILE_MACHINE_AMD64')) - - archDigest.setComponentByName('cpuSubType', 0) - - - for codeSectionHeader in coffFileHeader.codeSectionHeaders: - stream.bytepos = codeSectionHeader.offset - codeSectionBytes = stream.read('bytes:'+ str(codeSectionHeader.items['SizeOfRawData'])) - if codeSectionHeader.items['SizeOfRawData'] < codeSectionHeader.items['VirtualSize']: - #zero pad up to virtualSize - codeSectionBytes += "\0" * (codeSectionHeader.items['VirtualSize']-codeSectionHeader.items['SizeOfRawData']) + code_segment_digests = SetOfCodeSegmentDigest() + code_segment_idx = 0 + for code_sect_header in text_section_headers: + stream.bytepos = code_sect_header.offset + code_sect_bytes = stream.read('bytes:' + str(code_sect_header.items['VirtualSize'])) digester = hashlib.sha256() - digester.update(codeSectionBytes) + digester.update(code_sect_bytes) digest = digester.digest() - codeSectionDigest = CodeSectionDigest() - codeSectionDigest.setComponentByName('offset', codeSectionHeader.offset) - codeSectionDigest.setComponentByName('digestAlgorithm', univ.ObjectIdentifier('2.16.840.1.101.3.4.2.1')) - codeSectionDigest.setComponentByName('digest', univ.OctetString(digest)) + # with open('segment_' + str(code_sect_header.offset) + ".bin", 'wb') as f: + # f.write(code_sect_bytes) - setOfDigest = SetOfCodeSectionDigest() - setOfDigest.setComponentByPosition(0, codeSectionDigest) + code_section_digest = CodeSectionDigest() + code_section_digest.setComponentByName('offset', code_sect_header.offset) + code_section_digest.setComponentByName('digestAlgorithm', univ.ObjectIdentifier('2.16.840.1.101.3.4.2.1')) + code_section_digest.setComponentByName('digest', univ.OctetString(digest)) + + set_of_digest = SetOfCodeSectionDigest() + set_of_digest.setComponentByPosition(0, code_section_digest) codeSegmentDigest = CodeSegmentDigest() - codeSegmentDigest.setComponentByName('offset', codeSectionHeader.offset) - codeSegmentDigest.setComponentByName('codeSectionDigests', setOfDigest) + codeSegmentDigest.setComponentByName('offset', code_sect_header.offset) + codeSegmentDigest.setComponentByName('codeSectionDigests', set_of_digest) - codeSegmentDigests.setComponentByPosition(codeSegmentIdx, codeSegmentDigest) - codeSegmentIdx += 1 + code_segment_digests.setComponentByPosition(code_segment_idx, codeSegmentDigest) + code_segment_idx += 1 - archDigest.setComponentByName('CodeSegmentDigests', codeSegmentDigests) + arch_digest.setComponentByName('CodeSegmentDigests', code_segment_digests) setOfArchDigests = SetOfArchitectureDigest() - setOfArchDigests.setComponentByPosition(0, archDigest) + setOfArchDigests.setComponentByPosition(0, arch_digest) appDigest = ApplicationDigest() appDigest.setComponentByName('version', 1) appDigest.setComponentByName('digests', setOfArchDigests) - binaryDigest = encoder.encode(appDigest) + binaryDigest = der_encoder.encode(appDigest) - outFile = open(app_args.output, 'wb') - outFile.write(binaryDigest) + with open(app_args.output, 'wb') as f: + f.write(binaryDigest) + + # sign with openssl if specified + if app_args.openssl_path is not None: + assert app_args.signer_cert is not None + + out_base, out_ext = os.path.splitext(app_args.output) + signed_path = out_base + ".signed" + out_ext + + # http://stackoverflow.com/questions/12507277/how-to-fix-unable-to-write-random-state-in-openssl + temp_file = None + if sys.platform == "win32" and "RANDFILE" not in os.environ: + temp_file = create_temp_file() + os.environ["RANDFILE"] = temp_file + + try: + subprocess.check_call([app_args.openssl_path, "cms", "-sign", "-nodetach", "-md", "sha256", "-binary", "-in", app_args.output, "-outform", "der", "-out", signed_path, "-signer", app_args.signer_cert], ) + finally: + if temp_file is not None: + del os.environ["RANDFILE"] + os.unlink(temp_file) if __name__ == '__main__': main() From 7541c98f4d268ffc6e671fe63b873e799479c9be Mon Sep 17 00:00:00 2001 From: Chris Pearce Date: Sat, 21 Feb 2015 16:43:52 +1300 Subject: [PATCH 03/48] Bug 1134913 - Disable EME plugin-container voucher generation on Win64. r=ted --- toolkit/mozapps/installer/make-eme.mk | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/toolkit/mozapps/installer/make-eme.mk b/toolkit/mozapps/installer/make-eme.mk index aa113e8064e..eb7283cb1cb 100644 --- a/toolkit/mozapps/installer/make-eme.mk +++ b/toolkit/mozapps/installer/make-eme.mk @@ -6,9 +6,11 @@ include $(MOZILLA_DIR)/toolkit/mozapps/installer/signing.mk ifdef MOZ_SIGN_CMD ifeq ($(OS_ARCH),WINNT) - # The argument to this macro is the directory where plugin-container.exe - # exists, and where voucher.bin will be generated. - MAKE_SIGN_EME_VOUCHER = $(PYTHON) $(MOZILLA_DIR)/python/eme/gen-eme-voucher.py -input $(1)/plugin-container.exe -output $(1)/voucher.bin && \ - $(MOZ_SIGN_CMD) -f emevoucher "$(1)/voucher.bin" + ifneq ($(TARGET_CPU),x86_64) + # The argument to this macro is the directory where plugin-container.exe + # exists, and where voucher.bin will be generated. + MAKE_SIGN_EME_VOUCHER = $(PYTHON) $(MOZILLA_DIR)/python/eme/gen-eme-voucher.py -input $(1)/plugin-container.exe -output $(1)/voucher.bin && \ + $(MOZ_SIGN_CMD) -f emevoucher "$(1)/voucher.bin" + endif endif endif From 27fe54a5bc706bf4f64c495642cd79266647e266 Mon Sep 17 00:00:00 2001 From: Mats Palmgren Date: Sat, 21 Feb 2015 04:27:59 +0000 Subject: [PATCH 04/48] Bug 1129078 - part 1, Remove the chunk that bug 739396 added to Selection::Extend. r=smaug --- layout/generic/nsSelection.cpp | 42 ---------------------------------- 1 file changed, 42 deletions(-) diff --git a/layout/generic/nsSelection.cpp b/layout/generic/nsSelection.cpp index 5972b62ecb6..958de1c7d60 100644 --- a/layout/generic/nsSelection.cpp +++ b/layout/generic/nsSelection.cpp @@ -5001,48 +5001,6 @@ Selection::Extend(nsINode& aParentNode, uint32_t aOffset, ErrorResult& aRv) } nsDirection dir = GetDirection(); - - // If aParentNode is inside a range in a multi-range selection we need - // to remove the ranges that follows in the selection direction and - // make that range the mAnchorFocusRange. - if (mRanges.Length() > 1) { - for (size_t i = 0; i < mRanges.Length(); ++i) { - nsRange* range = mRanges[i].mRange; - bool disconnected1 = false; - bool disconnected2 = false; - const bool isBeforeStart = - nsContentUtils::ComparePoints(range->GetStartParent(), - range->StartOffset(), - &aParentNode, aOffset, - &disconnected1) > 0; - const bool isAfterEnd = - nsContentUtils::ComparePoints(range->GetEndParent(), - range->EndOffset(), - &aParentNode, aOffset, - &disconnected2) < 0; - if (!isBeforeStart && !isAfterEnd && !disconnected1 && !disconnected2) { - // aParentNode/aOffset is inside 'range'. - mAnchorFocusRange = range; - if (dir == eDirNext) { - for (size_t j = i + 1; j < mRanges.Length(); ++j) { - nsRange* r = mRanges[j].mRange; - r->SetInSelection(false); - selectFrames(presContext, r, false); - } - mRanges.TruncateLength(i + 1); - } else { - for (size_t j = 0; j < i; ++j) { - nsRange* r = mRanges[j].mRange; - r->SetInSelection(false); - selectFrames(presContext, r, false); - } - mRanges.RemoveElementsAt(0, i); - } - break; - } - } - } - nsINode* anchorNode = GetAnchorNode(); nsINode* focusNode = GetFocusNode(); uint32_t anchorOffset = AnchorOffset(); From 85b48cacb6f54c6e7df41b78ec013213ad1c2752 Mon Sep 17 00:00:00 2001 From: Mats Palmgren Date: Sat, 21 Feb 2015 04:27:59 +0000 Subject: [PATCH 05/48] Bug 1129078 - part 2, Refactor nsFrameSelection::MoveCaret slightly. r=smaug --- layout/generic/nsSelection.cpp | 80 +++++++++++++++++----------------- 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/layout/generic/nsSelection.cpp b/layout/generic/nsSelection.cpp index 958de1c7d60..7191ee27d23 100644 --- a/layout/generic/nsSelection.cpp +++ b/layout/generic/nsSelection.cpp @@ -774,13 +774,6 @@ nsFrameSelection::MoveCaret(nsDirection aDirection, if (NS_FAILED(result)) { return result; } - if (aAmount == eSelectLine) { - result = FetchDesiredPos(desiredPos); - if (NS_FAILED(result)) { - return result; - } - SetDesiredPos(desiredPos); - } int32_t caretStyle = Preferences::GetInt("layout.selection.caret_style", 0); if (caretStyle == 0 @@ -792,39 +785,46 @@ nsFrameSelection::MoveCaret(nsDirection aDirection, caretStyle = 2; } - if (!isCollapsed && !aContinueSelection && caretStyle == 2 && - aAmount <= eSelectLine) { - switch (aDirection) { - case eDirPrevious: - { - const nsRange* anchorFocusRange = sel->GetAnchorFocusRange(); - if (anchorFocusRange) { - PostReason(nsISelectionListener::COLLAPSETOSTART_REASON); - sel->Collapse(anchorFocusRange->GetStartParent(), - anchorFocusRange->StartOffset()); - } - mHint = CARET_ASSOCIATE_AFTER; - sel->ScrollIntoView(nsISelectionController::SELECTION_FOCUS_REGION, - nsIPresShell::ScrollAxis(), - nsIPresShell::ScrollAxis(), scrollFlags); - return NS_OK; - } - - case eDirNext: - { - const nsRange* anchorFocusRange = sel->GetAnchorFocusRange(); - if (anchorFocusRange) { - PostReason(nsISelectionListener::COLLAPSETOEND_REASON); - sel->Collapse(anchorFocusRange->GetEndParent(), - anchorFocusRange->EndOffset()); - } - mHint = CARET_ASSOCIATE_BEFORE; - sel->ScrollIntoView(nsISelectionController::SELECTION_FOCUS_REGION, - nsIPresShell::ScrollAxis(), - nsIPresShell::ScrollAxis(), scrollFlags); - return NS_OK; - } + bool doCollapse = !isCollapsed && !aContinueSelection && caretStyle == 2 && + aAmount <= eSelectLine; + if (doCollapse) { + if (aDirection == eDirPrevious) { + PostReason(nsISelectionListener::COLLAPSETOSTART_REASON); + mHint = CARET_ASSOCIATE_AFTER; + } else { + PostReason(nsISelectionListener::COLLAPSETOEND_REASON); + mHint = CARET_ASSOCIATE_BEFORE; } + } else { + PostReason(nsISelectionListener::KEYPRESS_REASON); + } + + if (aAmount == eSelectLine) { + result = FetchDesiredPos(desiredPos); + if (NS_FAILED(result)) { + return result; + } + SetDesiredPos(desiredPos); + } + + if (doCollapse) { + const nsRange* anchorFocusRange = sel->GetAnchorFocusRange(); + if (anchorFocusRange) { + nsINode* node; + int32_t offset; + if (aDirection == eDirPrevious) { + node = anchorFocusRange->GetStartParent(); + offset = anchorFocusRange->StartOffset(); + } else { + node = anchorFocusRange->GetEndParent(); + offset = anchorFocusRange->EndOffset(); + } + sel->Collapse(node, offset); + } + sel->ScrollIntoView(nsISelectionController::SELECTION_FOCUS_REGION, + nsIPresShell::ScrollAxis(), + nsIPresShell::ScrollAxis(), scrollFlags); + return NS_OK; } nsIFrame *frame; @@ -868,7 +868,7 @@ nsFrameSelection::MoveCaret(nsDirection aDirection, default: return NS_ERROR_FAILURE; } - PostReason(nsISelectionListener::KEYPRESS_REASON); + if (NS_SUCCEEDED(result = frame->PeekOffset(&pos)) && pos.mResultContent) { nsIFrame *theFrame; From 71b97d01a08005db784a506741b588eaaefe147a Mon Sep 17 00:00:00 2001 From: Mats Palmgren Date: Sat, 21 Feb 2015 04:27:59 +0000 Subject: [PATCH 06/48] Bug 1129078 - part 3, Add a mIsGenerated bit to ranges that ExcludeNonSelectableNodes created due to user-select:none. Also, return the index to the range we want to be the new mAnchorFocusRange, based on the Selection direction. r=smaug --- dom/base/nsRange.h | 23 +++++++++++++++++++++++ layout/generic/nsSelection.cpp | 12 +++++++++++- 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/dom/base/nsRange.h b/dom/base/nsRange.h index 0b4da9356d5..e9a02a86333 100644 --- a/dom/base/nsRange.h +++ b/dom/base/nsRange.h @@ -49,6 +49,7 @@ public: , mIsDetached(false) , mMaySpanAnonymousSubtrees(false) , mInSelection(false) + , mIsGenerated(false) , mStartOffsetWasIncremented(false) , mEndOffsetWasIncremented(false) , mEnableGravitationOnElementRemoval(true) @@ -153,6 +154,27 @@ public: } } + /** + * Return true if this range was generated. + * @see SetIsGenerated + */ + bool IsGenerated() const + { + return mIsGenerated; + } + + /** + * Mark this range as being generated or not. + * Currently it is used for marking ranges that are created when splitting up + * a range to exclude a -moz-user-select:none region. + * @see Selection::AddItem + * @see ExcludeNonSelectableNodes + */ + void SetIsGenerated(bool aIsGenerated) + { + mIsGenerated = aIsGenerated; + } + nsINode* GetCommonAncestor() const; void Reset(); nsresult SetStart(nsINode* aParent, int32_t aOffset); @@ -333,6 +355,7 @@ protected: bool mIsDetached; bool mMaySpanAnonymousSubtrees; bool mInSelection; + bool mIsGenerated; bool mStartOffsetWasIncremented; bool mEndOffsetWasIncremented; bool mEnableGravitationOnElementRemoval; diff --git a/layout/generic/nsSelection.cpp b/layout/generic/nsSelection.cpp index 7191ee27d23..ec337090fdb 100644 --- a/layout/generic/nsSelection.cpp +++ b/layout/generic/nsSelection.cpp @@ -3512,9 +3512,19 @@ Selection::AddItem(nsRange* aItem, int32_t* aOutIndex) rangesToAdd.AppendElement(aItem); } } + *aOutIndex = -1; + size_t newAnchorFocusIndex = + GetDirection() == eDirPrevious ? 0 : rangesToAdd.Length() - 1; for (size_t i = 0; i < rangesToAdd.Length(); ++i) { - nsresult rv = AddItemInternal(rangesToAdd[i], aOutIndex); + int32_t index; + nsresult rv = AddItemInternal(rangesToAdd[i], &index); NS_ENSURE_SUCCESS(rv, rv); + if (i == newAnchorFocusIndex) { + *aOutIndex = index; + rangesToAdd[i]->SetIsGenerated(false); + } else { + rangesToAdd[i]->SetIsGenerated(true); + } } return NS_OK; } From 17c60885ea0d84d668d55fbd00b90288c87cf346 Mon Sep 17 00:00:00 2001 From: Mats Palmgren Date: Sat, 21 Feb 2015 04:27:59 +0000 Subject: [PATCH 07/48] Bug 1129078 - part 4, Add AutoPrepareFocusRange stack objects on paths to TakeFocus that sets up mAnchorFocusRange (and possibly removes mIsGenerated ranges) based on what operation the user is performing. r=smaug --- layout/generic/Selection.h | 3 +- layout/generic/nsFrameSelection.h | 1 + layout/generic/nsSelection.cpp | 101 ++++++++++++++++++++++++++++++ 3 files changed, 104 insertions(+), 1 deletion(-) diff --git a/layout/generic/Selection.h b/layout/generic/Selection.h index 4987df94aa3..ccdcfdf57cf 100644 --- a/layout/generic/Selection.h +++ b/layout/generic/Selection.h @@ -27,6 +27,7 @@ struct SelectionDetails; namespace mozilla { class ErrorResult; +struct AutoPrepareFocusRange; } struct RangeData @@ -226,7 +227,7 @@ public: MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER }; private: - + friend struct mozilla::AutoPrepareFocusRange; class ScrollSelectionIntoViewEvent; friend class ScrollSelectionIntoViewEvent; diff --git a/layout/generic/nsFrameSelection.h b/layout/generic/nsFrameSelection.h index 859177ee050..c7aa1485320 100644 --- a/layout/generic/nsFrameSelection.h +++ b/layout/generic/nsFrameSelection.h @@ -642,6 +642,7 @@ private: } friend class mozilla::dom::Selection; + friend struct mozilla::AutoPrepareFocusRange; #ifdef DEBUG void printSelection(); // for debugging #endif /* DEBUG */ diff --git a/layout/generic/nsSelection.cpp b/layout/generic/nsSelection.cpp index ec337090fdb..5979c0863ab 100644 --- a/layout/generic/nsSelection.cpp +++ b/layout/generic/nsSelection.cpp @@ -326,6 +326,101 @@ IsValidSelectionPoint(nsFrameSelection *aFrameSel, nsINode *aNode) return !limiter || nsContentUtils::ContentIsDescendantOf(aNode, limiter); } +namespace mozilla { +struct MOZ_STACK_CLASS AutoPrepareFocusRange +{ + AutoPrepareFocusRange(Selection* aSelection, + bool aContinueSelection, + bool aMultipleSelection + MOZ_GUARD_OBJECT_NOTIFIER_PARAM) + { + MOZ_GUARD_OBJECT_NOTIFIER_INIT; + + if (aSelection->mRanges.Length() <= 1) { + return; + } + + if (aSelection->mFrameSelection->IsUserSelectionReason()) { + mUserSelect.emplace(aSelection); + } + bool userSelection = aSelection->mApplyUserSelectStyle; + + nsTArray& ranges = aSelection->mRanges; + if (!userSelection || + (!aContinueSelection && aMultipleSelection)) { + // Scripted command or the user is starting a new explicit multi-range + // selection. + for (RangeData& entry : ranges) { + entry.mRange->SetIsGenerated(false); + } + return; + } + + int16_t reason = aSelection->mFrameSelection->mSelectionChangeReason; + bool isAnchorRelativeOp = (reason & (nsISelectionListener::DRAG_REASON | + nsISelectionListener::MOUSEDOWN_REASON | + nsISelectionListener::MOUSEUP_REASON | + nsISelectionListener::COLLAPSETOSTART_REASON)); + if (!isAnchorRelativeOp) { + return; + } + + // This operation is against the anchor but our current mAnchorFocusRange + // represents the focus in a multi-range selection. The anchor from a user + // perspective is the most distant generated range on the opposite side. + // Find that range and make it the mAnchorFocusRange. + const size_t len = ranges.Length(); + size_t newAnchorFocusIndex = size_t(-1); + if (aSelection->GetDirection() == eDirNext) { + for (size_t i = 0; i < len; ++i) { + if (ranges[i].mRange->IsGenerated()) { + newAnchorFocusIndex = i; + break; + } + } + } else { + size_t i = len; + while (i--) { + if (ranges[i].mRange->IsGenerated()) { + newAnchorFocusIndex = i; + break; + } + } + } + + if (newAnchorFocusIndex == size_t(-1)) { + // There are no generated ranges - that's fine. + return; + } + + // Setup the new mAnchorFocusRange and mark the old one as generated. + if (aSelection->mAnchorFocusRange) { + aSelection->mAnchorFocusRange->SetIsGenerated(true); + } + nsRange* range = ranges[newAnchorFocusIndex].mRange; + range->SetIsGenerated(false); + aSelection->mAnchorFocusRange = range; + + // Remove all generated ranges (including the old mAnchorFocusRange). + nsRefPtr presContext = aSelection->GetPresContext(); + size_t i = len; + while (i--) { + range = aSelection->mRanges[i].mRange; + if (range->IsGenerated()) { + range->SetInSelection(false); + aSelection->selectFrames(presContext, range, false); + aSelection->mRanges.RemoveElementAt(i); + } + } + if (aSelection->mFrameSelection) { + aSelection->mFrameSelection->InvalidateDesiredPos(); + } + } + + Maybe mUserSelect; + MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER +}; +} ////////////BEGIN nsFrameSelection methods @@ -799,6 +894,8 @@ nsFrameSelection::MoveCaret(nsDirection aDirection, PostReason(nsISelectionListener::KEYPRESS_REASON); } + AutoPrepareFocusRange prep(sel, aContinueSelection, false); + if (aAmount == eSelectLine) { result = FetchDesiredPos(desiredPos); if (NS_FAILED(result)) { @@ -1376,6 +1473,8 @@ nsFrameSelection::HandleClick(nsIContent* aNewFocus, AdjustForMaintainedSelection(aNewFocus, aContentOffset)) return NS_OK; //shift clicked to maintained selection. rejected. + int8_t index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL); + AutoPrepareFocusRange prep(mDomSelections[index], aContinueSelection, aMultipleSelection); return TakeFocus(aNewFocus, aContentOffset, aContentEndOffset, aHint, aContinueSelection, aMultipleSelection); } @@ -2050,6 +2149,8 @@ nsFrameSelection::SelectAll() } int32_t numChildren = rootContent->GetChildCount(); PostReason(nsISelectionListener::NO_REASON); + int8_t index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL); + AutoPrepareFocusRange prep(mDomSelections[index], false, false); return TakeFocus(rootContent, 0, numChildren, CARET_ASSOCIATE_BEFORE, false, false); } From 3d394f90b03dda265c01142cc65d105dc92518ee Mon Sep 17 00:00:00 2001 From: Mats Palmgren Date: Sat, 21 Feb 2015 04:27:59 +0000 Subject: [PATCH 08/48] Bug 1129078 - part 5, Test suite for multi-range selections involving user-select:none. --- layout/base/tests/Ahem.ttf | Bin 0 -> 12480 bytes layout/base/tests/mochitest.ini | 4 + .../tests/multi-range-user-select-ref.html | 158 ++++++++++++++ .../base/tests/multi-range-user-select.html | 198 ++++++++++++++++++ layout/base/tests/selection-utils.js | 151 +++++++++++++ .../base/tests/test_reftests_with_caret.html | 62 +++++- 6 files changed, 572 insertions(+), 1 deletion(-) create mode 100644 layout/base/tests/Ahem.ttf create mode 100644 layout/base/tests/multi-range-user-select-ref.html create mode 100644 layout/base/tests/multi-range-user-select.html create mode 100644 layout/base/tests/selection-utils.js diff --git a/layout/base/tests/Ahem.ttf b/layout/base/tests/Ahem.ttf new file mode 100644 index 0000000000000000000000000000000000000000..ac81cb03165ab831a36abb59145ff7a2f5675fb9 GIT binary patch literal 12480 zcmeHNZFm&b6@KSqXFq`?V#J8_W{Z(327;C%rN~F5ND+}BqNU0vJIMyKyK#48f&xWr z)kgU!T8&C6B7%Y<3R(n85fK4VsitTVmo!>UZB&Y2eGpjRJ3A2++dqB$VLP+W?3{b< zy=Tt7=bd}c?z~U{_%Rb2`d(7jr(eU^QL_ML0JW1VqM_O@yYfc>t{T8dRE3hYI0asy z?@HF^RMkwayl7QwHx0A^1>uMp3h!DpraSA_^Xwm?!tK-ZDIeh3GZIZTJR>lj_L~5$ zxh7r_5_M+=fud6C+M}U{T6`+)E8W;$#6nSXM%O#m0KNM1{*l^vGBs=G>5V`!`>Wj` zeOE9n@03fcuI@7EAiP=|HCPqD`U-j7c+T>RfXo1`O%p6?P^Fd!`)MLGfZtYoeoITo z9|tYXL3r#wU#;iuwKiogTol%;^ayjZSLJ#1Q#5L!a~$%R2|S(yG14mV>+*Al14AJ8 zskRRSTLqNdA*|Dc<*Bed<2+?OlwLZgGY1X^zTFs*k8}3aN82k-cWSt{s6{YTpM2g~ z`084ta52dq9ap<}!X?{NgU)ryfSaqAT!VaE-vj7`&gg<1CKG*VYI28pr z4c&1%df+<{C`1t7MNgc8BAkg{_#V!}+31aPP>eq4ixTw1xi}BsM}PbP18_bD;)fW7 zA7L;q;JP*x!!R7BxDX@oV~oT_7=@o;G%m&%T!OK<6ys2a@wf~Va5*O83S5bwViJCa zt8g_Y)87!vQGqZ_RH6zIRAUORK@FmaA&y#Hiv*HLp$^xf9u1g^>v037abKB%pL4l= z6F1@}`~o-QE&LL<;8xs*xA7}9;(gqXJJ5z%_y^iC8*}gu-o@Y0jK5pKVui(z+doNEWq#ZAr@jMTJZo{@K-A$d$0(v zV>kBVK`h2YSc0W^7>{5Xmg7-8#x^VPI9B2bJc(6Uji>N5evdVH25Yg7JEu-GfU`A( zQS8hp%s@sdQOcACWuE$ix=npcyGwgSTd!>tIijm55Isat6p6lKpeQX278V87pb_j6 z%nKF-dj!u8jtou?n!){NY~QD5GPElg(}lD*&a$_K_TD(o-btdH=q`k1@BA;>%MG6T zg}tz451CBn?My3o*c&s`G7~cw07u$+4nA_$;n$9I-J5CI1hlMgS=X|r}@&0b7ZTImF2#&i@u-pb6Wt4KES}_c3-4GU^hK zw-b>|iOI!`=5jll8;H;qRz&9$sm~KxPgrrS!bWT&?y8C1Da7qHMD9Fdx0dLY*i8|= z61#sSdZ!Y<*Au%p5J`73($g&jKa1yxs#U~778&b^lC>7zUMF5R)7SSLlwIg)X8d0ou)RbP3jVLwYo{&sixI-^^oS#^0l5? zKW&IMMw_Hf(HgW{_#S(awo==m?a+49(@%6=&(nootPj#h=@a#^p3rCNv-SDgDS1Ds_!>O?Jgx(_D?NCf5?zYS$*$PFLF1?mA?6jC`Y~(a#uS zj4>t|Q;Y`V7UM2sk+IU)VC*n<8*RoXZrz>d7VcvAAonQuM0ce-<-W;1*S)~~sC%t@ zt9zIG9ru0@Je@t=J!g3a@H4_Q-c#wVjOZ}{Hz9q_CE9DfggZ~s95NdE+X*q`vv^w0Ls_b>CW@o(`r`&<3{{6_ul3(%P!uhX4o>vqA#{(jzxde zmN^!Etu1pb`fgk1SoBs~=2-MkZ8--Xm;4`7+RsI2m&ca#sLv-OCT{Uq1i1wfvJ4VD zE65HoEq;rzG$sT!5$XibD8G~B5Y*Xqy)A76dzoYxq*y8_g)Iq%2(Eaogq z8&R^AOtO&Vm}G<|Sgob!S+7gzX$h@$wC=cmeLK7MV)9r;MI@96Riw;BQiKwwNJUIx zMn&@4I+l71v!TMQO^KolhgQ_3Oc9EOMX)y5ThvFYDB4 zsAZ)*lr(FqV`j0qENNEO)riV?LZr-OsybFBD&kSHXsc7z@t8}O@731UKuow}FBw}VF+mntBFDnyep=ezqRDEVrlqcf#N!Hpni-$(c=s2^gt|pY2 zz~M;KCB-E|s^xi{u0v(FmPO|&_Xp^ zka_nJ_Tit{kB`anKZsAPxJh>7I&$S6Ca>*&i|4r6VmdxgM&2f}@0OEmw}A|}^<>R8 zk!g1?xpwo&ubgACFPD;w_bOR*&yq>6D!M-AveBc}k>j<}3rEwsX0h^fppeg~eGU!H Bc@h8s literal 0 HcmV?d00001 diff --git a/layout/base/tests/mochitest.ini b/layout/base/tests/mochitest.ini index f560c94c62f..dae2fe672af 100644 --- a/layout/base/tests/mochitest.ini +++ b/layout/base/tests/mochitest.ini @@ -2,6 +2,7 @@ # Android: SLOW_DIRECTORY; Mulet: bug 1048441, bug 1087611, bug 1112988, etc. skip-if = toolkit == 'android' || buildapp == 'mulet' support-files = + Ahem.ttf border_radius_hit_testing_iframe.html preserve3d_sorting_hit_testing_iframe.html image_rgrg-256x256.png @@ -69,6 +70,9 @@ support-files = bug1123067-2.html bug1123067-3.html bug1123067-ref.html + selection-utils.js + multi-range-user-select.html + multi-range-user-select-ref.html [test_preserve3d_sorting_hit_testing.html] [test_after_paint_pref.html] diff --git a/layout/base/tests/multi-range-user-select-ref.html b/layout/base/tests/multi-range-user-select-ref.html new file mode 100644 index 00000000000..990ca33525e --- /dev/null +++ b/layout/base/tests/multi-range-user-select-ref.html @@ -0,0 +1,158 @@ + + + + Testcase #1 for bug 1129078 + + + + + + + +
+2af45494-ak7e-11e4-a0c6-a7e7
+38222880-bj6d-11e4-8064-fb7b
+3d649ae4-ci5c-11e4-995d-17b2
+434351bc-dh4b-11e4-9971-4fc8
+4dc0e0b4-eg4a-11e4-8c28-5319
+a96319c8-ad7d-11e4-b312-039c
+
+ +

+
+
+
+
+
diff --git a/layout/base/tests/multi-range-user-select.html b/layout/base/tests/multi-range-user-select.html
new file mode 100644
index 00000000000..89814abbd7e
--- /dev/null
+++ b/layout/base/tests/multi-range-user-select.html
@@ -0,0 +1,198 @@
+
+
+    
+    Testcase #1 for bug 1129078
+    
+    
+    
+
+
+
+
+
+
+2af45494-ak7e-11e4-a0c6-a7e7
+38222880-bj6d-11e4-8064-fb7b
+3d649ae4-ci5c-11e4-995d-17b2
+434351bc-dh4b-11e4-9971-4fc8
+4dc0e0b4-eg4a-11e4-8c28-5319
+a96319c8-ad7d-11e4-b312-039c
+
+ +

+
+
+
+
+
diff --git a/layout/base/tests/selection-utils.js b/layout/base/tests/selection-utils.js
new file mode 100644
index 00000000000..e1ac8eb6d02
--- /dev/null
+++ b/layout/base/tests/selection-utils.js
@@ -0,0 +1,151 @@
+// -*- Mode: Javascript; tab-width: 2; indent-tabs-mode: nil; js-indent-level: 2 -*-
+// vim: set ts=2 sw=2 et tw=78:
+function clearSelection(w)
+{
+  var sel = (w ? w : window).getSelection();
+  sel.removeAllRanges();
+}
+
+function getNode(e, index) { 
+  if (!(typeof index==='number')) return index;
+  if (index >= 0) return e.childNodes[index];
+  while (++index != 0) e = e.parentNode;
+  return e;
+}
+
+function dragSelectPointsWithData()
+{
+  var event = arguments[0];
+  var e = arguments[1];
+  var x1 = arguments[2];
+  var y1 = arguments[3];
+  var x2 = arguments[4];
+  var y2 = arguments[5];
+  dir = x2 > x1 ? 1 : -1;
+  event['type'] = "mousedown";
+  synthesizeMouse(e, x1, y1, event);
+  event['type'] = "mousemove";
+  synthesizeMouse(e, x1 + dir, y1, event);
+  for (var i = 6; i < arguments.length; ++i)
+    synthesizeMouse(e, arguments[i], y1, event);
+  synthesizeMouse(e, x2 - dir, y2, event);
+  event['type'] = "mouseup";
+  synthesizeMouse(e, x2, y2, event);
+}
+
+function dragSelectPoints()
+{
+  var args = Array.prototype.slice.call(arguments);
+  dragSelectPointsWithData.apply(this, [{}].concat(args));
+}
+
+function dragSelect()
+{
+  var args = Array.prototype.slice.call(arguments);
+  var e = args.shift();
+  var x1 = args.shift();
+  var x2 = args.shift();
+  dragSelectPointsWithData.apply(this, [{},e,x1,5,x2,5].concat(args));
+}
+
+function accelDragSelect()
+{
+  var args = Array.prototype.slice.call(arguments);
+  var e = args.shift();
+  var x1 = args.shift();
+  var x2 = args.shift();
+  var y = args.length != 0 ? args.shift() : 5;
+  dragSelectPointsWithData.apply(this, [{accelKey: true},e,x1,y,x2,y].concat(args));
+}
+
+function shiftClick(e, x, y)
+{
+  function pos(p) { return (typeof p === "undefined") ? 5 : p }
+  synthesizeMouse(e, pos(x), pos(y), { shiftKey: true });
+}
+
+function keyRepeat(key,data,repeat)
+{
+  while (repeat-- > 0)
+    synthesizeKey(key, data);
+}
+
+function keyRight(data)
+{
+  var repeat = arguments.length > 1 ? arguments[1] : 1;
+  keyRepeat("VK_RIGHT", data, repeat);
+}
+
+function keyLeft(data)
+{
+  var repeat = arguments.length > 1 ? arguments[1] : 1;
+  keyRepeat("VK_LEFT", data, repeat);
+}
+
+function shiftAccelClick(e, x, y)
+{
+  function pos(p) { return (typeof p === "undefined") ? 5 : p }
+  synthesizeMouse(e, pos(x), pos(y), { shiftKey: true, accelKey: true });
+}
+
+function accelClick(e, x, y)
+{
+  function pos(p) { return (typeof p === "undefined") ? 5 : p }
+  synthesizeMouse(e, pos(x), pos(y), { accelKey: true });
+}
+
+function addChildRanges(arr, e)
+{
+  var sel = window.getSelection();
+  for (i = 0; i < arr.length; ++i) {
+    var data = arr[i];
+    var r = new Range()
+    r.setStart(getNode(e, data[0]), data[1]);
+    r.setEnd(getNode(e, data[2]), data[3]);
+    sel.addRange(r);
+  }
+}
+
+function checkText(text, e)
+{
+  var sel = window.getSelection();
+  is(sel.toString(), text, e.id + ": selected text")
+}
+
+function checkRangeText(text, index)
+{
+  var r = window.getSelection().getRangeAt(index);
+  is(r.toString(), text, e.id + ": range["+index+"].toString()")
+}
+
+function checkRangeCount(n, e)
+{
+  var sel = window.getSelection();
+  is(sel.rangeCount, n, e.id + ": Selection range count");
+}
+
+function checkRangePoints(r, expected, e) {
+  is(r.startContainer, expected[0], e.id + ": range.startContainer");
+  is(r.startOffset, expected[1], e.id + ": range.startOffset");
+  is(r.endContainer, expected[2], e.id + ": range.endContainer");
+  is(r.endOffset, expected[3], e.id + ": range.endOffset");
+}
+
+function checkRange(i, expected, e) {
+  var sel = window.getSelection();
+  if (i >= sel.rangeCount) return;
+  var r = sel.getRangeAt(i);
+  is(r.startContainer, getNode(e, expected[0]), e.id + ": range["+i+"].startContainer");
+  is(r.startOffset, expected[1], e.id + ": range["+i+"].startOffset");
+  is(r.endContainer, getNode(e, expected[2]), e.id + ": range["+i+"].endContainer");
+  is(r.endOffset, expected[3], e.id + ": range["+i+"].endOffset");
+}
+
+function checkRanges(arr, e)
+{
+  checkRangeCount(arr.length, e);
+  for (i = 0; i < arr.length; ++i) {
+    var expected = arr[i];
+    checkRange(i, expected, e);
+  }
+}
diff --git a/layout/base/tests/test_reftests_with_caret.html b/layout/base/tests/test_reftests_with_caret.html
index 491384a2131..0fe6f983f69 100644
--- a/layout/base/tests/test_reftests_with_caret.html
+++ b/layout/base/tests/test_reftests_with_caret.html
@@ -8,8 +8,9 @@
   
   
 
+    
+
+
+
+
+
+
+2af45494-ak7e-11e4-a0c6-a7e7
+38222880-bj6d-11e4-8064-fb7b
+3d649ae4-ci5c-11e4-995d-17b2
+434351bc-dh4b-11e4-9971-4fc8
+4dc0e0b4-eg4a-11e4-8c28-5319
+a96319c8-ad7d-11e4-b312-039c
+
+ +

+
+
+
+
+
diff --git a/layout/base/tests/multi-range-script-select.html b/layout/base/tests/multi-range-script-select.html
new file mode 100644
index 00000000000..b9bcaeab886
--- /dev/null
+++ b/layout/base/tests/multi-range-script-select.html
@@ -0,0 +1,185 @@
+
+
+    
+    Testcase #1 for bug 1129078
+    
+    
+    
+
+
+
+
+
+
+2af45494-ak7e-11e4-a0c6-a7e7
+38222880-bj6d-11e4-8064-fb7b
+3d649ae4-ci5c-11e4-995d-17b2
+434351bc-dh4b-11e4-9971-4fc8
+4dc0e0b4-eg4a-11e4-8c28-5319
+a96319c8-ad7d-11e4-b312-039c
+
+ +

+
+
+
+
+
diff --git a/layout/base/tests/multi-range-user-select-ref.html b/layout/base/tests/multi-range-user-select-ref.html
index 990ca33525e..b50ffc65071 100644
--- a/layout/base/tests/multi-range-user-select-ref.html
+++ b/layout/base/tests/multi-range-user-select-ref.html
@@ -117,8 +117,8 @@ function runTest() {
         setupSelectionNext3();
         sel.extend(e.firstChild, 162);
       } else if (op == "AD") {
-       setupSelectionNext3();
-       addChildRanges([[0,1,0,2]], e);
+        setupSelectionNext3();
+        addChildRanges([[0,1,0,2]], e);
       } else {
         setupSelectionNext1();
         sel.extend(e.firstChild, 1);
diff --git a/layout/base/tests/test_reftests_with_caret.html b/layout/base/tests/test_reftests_with_caret.html
index 0fe6f983f69..3672c654078 100644
--- a/layout/base/tests/test_reftests_with_caret.html
+++ b/layout/base/tests/test_reftests_with_caret.html
@@ -219,6 +219,50 @@ if (navigator.platform.indexOf("Linux") >= 0 &&
     // eDirNext, VK_RIGHT / LEFT
     [ 'multi-range-user-select.html#next1SR' , 'multi-range-user-select-ref.html#next1SR'  ] ,
     [ 'multi-range-user-select.html#next1SL' , 'multi-range-user-select-ref.html#next1SL'  ] ,
+    // eDirPrevious, Shift+click
+    [ 'multi-range-script-select.html#prev1S_' , 'multi-range-script-select-ref.html#prev1S_'  ] ,
+    [ 'multi-range-script-select.html#prev2S_' , 'multi-range-script-select-ref.html#prev2S_'  ] ,
+    [ 'multi-range-script-select.html#prev3S_' , 'multi-range-script-select-ref.html#prev3S_'  ] ,
+    [ 'multi-range-script-select.html#prev4S_' , 'multi-range-script-select-ref.html#prev4S_'  ] ,
+    [ 'multi-range-script-select.html#prev5S_' , 'multi-range-script-select-ref.html#prev5S_'  ] ,
+    [ 'multi-range-script-select.html#prev6S_' , 'multi-range-script-select-ref.html#prev6S_'  ] ,
+    [ 'multi-range-script-select.html#prev7S_' , 'multi-range-script-select-ref.html#prev7S_'  ] ,
+    // eDirPrevious, Shift+Accel+click
+    [ 'multi-range-script-select.html#prev1SA' , 'multi-range-script-select-ref.html#prev1SA'  ] ,
+    [ 'multi-range-script-select.html#prev2SA' , 'multi-range-script-select-ref.html#prev2SA'  ] ,
+    [ 'multi-range-script-select.html#prev3SA' , 'multi-range-script-select-ref.html#prev3SA'  ] ,
+    [ 'multi-range-script-select.html#prev4SA' , 'multi-range-script-select-ref.html#prev4SA'  ] ,
+    [ 'multi-range-script-select.html#prev5SA' , 'multi-range-script-select-ref.html#prev5SA'  ] ,
+    [ 'multi-range-script-select.html#prev6SA' , 'multi-range-script-select-ref.html#prev6SA'  ] ,
+    [ 'multi-range-script-select.html#prev7SA' , 'multi-range-script-select-ref.html#prev7SA'  ] ,
+    // eDirPrevious, Accel+drag-select (adding an additional range)
+    [ 'multi-range-script-select.html#prev1AD' , 'multi-range-script-select-ref.html#prev1AD'  ] ,
+    [ 'multi-range-script-select.html#prev7AD' , 'multi-range-script-select-ref.html#prev7AD'  ] ,
+    // eDirPrevious, VK_RIGHT / LEFT
+    [ 'multi-range-script-select.html#prev1SR' , 'multi-range-script-select-ref.html#prev1SR'  ] ,
+    [ 'multi-range-script-select.html#prev1SL' , 'multi-range-script-select-ref.html#prev1SL'  ] ,
+    // eDirNext, Shift+click
+    [ 'multi-range-script-select.html#next1S_' , 'multi-range-script-select-ref.html#next1S_'  ] ,
+    [ 'multi-range-script-select.html#next2S_' , 'multi-range-script-select-ref.html#next2S_'  ] ,
+    [ 'multi-range-script-select.html#next3S_' , 'multi-range-script-select-ref.html#next3S_'  ] ,
+    [ 'multi-range-script-select.html#next4S_' , 'multi-range-script-select-ref.html#next4S_'  ] ,
+    [ 'multi-range-script-select.html#next5S_' , 'multi-range-script-select-ref.html#next5S_'  ] ,
+    [ 'multi-range-script-select.html#next6S_' , 'multi-range-script-select-ref.html#next6S_'  ] ,
+    [ 'multi-range-script-select.html#next7S_' , 'multi-range-script-select-ref.html#next7S_'  ] ,
+    // eDirNext, Shift+Accel+click
+    [ 'multi-range-script-select.html#next1SA' , 'multi-range-script-select-ref.html#next1SA'  ] ,
+    [ 'multi-range-script-select.html#next2SA' , 'multi-range-script-select-ref.html#next2SA'  ] ,
+    [ 'multi-range-script-select.html#next3SA' , 'multi-range-script-select-ref.html#next3SA'  ] ,
+    [ 'multi-range-script-select.html#next4SA' , 'multi-range-script-select-ref.html#next4SA'  ] ,
+    [ 'multi-range-script-select.html#next5SA' , 'multi-range-script-select-ref.html#next5SA'  ] ,
+    [ 'multi-range-script-select.html#next6SA' , 'multi-range-script-select-ref.html#next6SA'  ] ,
+    [ 'multi-range-script-select.html#next7SA' , 'multi-range-script-select-ref.html#next7SA'  ] ,
+    // eDirNext, Accel+drag-select (adding an additional range)
+    [ 'multi-range-script-select.html#next1AD' , 'multi-range-script-select-ref.html#next1AD'  ] ,
+    [ 'multi-range-script-select.html#next7AD' , 'multi-range-script-select-ref.html#next7AD'  ] ,
+    // eDirNext, VK_RIGHT / LEFT
+    [ 'multi-range-script-select.html#next1SR' , 'multi-range-script-select-ref.html#next1SR'  ] ,
+    [ 'multi-range-script-select.html#next1SL' , 'multi-range-script-select-ref.html#next1SL'  ] ,
     function() {SpecialPowers.pushPrefEnv({'clear': [['touchcaret.enabled']]}, nextTest);} ,
   ]);
 }

From 40f9adf83870caf1ce44c6cc2444f4b3c29bdce3 Mon Sep 17 00:00:00 2001
From: Mats Palmgren 
Date: Sat, 21 Feb 2015 04:27:59 +0000
Subject: [PATCH 10/48] Bug 1128722 - part 1, Call SetDirection as soon as we
 decide it needs to change.  r=smaug

---
 layout/generic/nsSelection.cpp | 28 +++++++++++++---------------
 1 file changed, 13 insertions(+), 15 deletions(-)

diff --git a/layout/generic/nsSelection.cpp b/layout/generic/nsSelection.cpp
index 5979c0863ab..112c0275e0c 100644
--- a/layout/generic/nsSelection.cpp
+++ b/layout/generic/nsSelection.cpp
@@ -5111,7 +5111,9 @@ Selection::Extend(nsINode& aParentNode, uint32_t aOffset, ErrorResult& aRv)
     return;
   }
 
-  nsDirection dir = GetDirection();
+#ifdef DEBUG_SELECTION
+  nsDirection oldDirection = GetDirection();
+#endif
   nsINode* anchorNode = GetAnchorNode();
   nsINode* focusNode = GetFocusNode();
   uint32_t anchorOffset = AnchorOffset();
@@ -5163,7 +5165,7 @@ Selection::Extend(nsINode& aParentNode, uint32_t aOffset, ErrorResult& aRv)
     if (aRv.Failed()) {
       return;
     }
-    dir = eDirNext;
+    SetDirection(eDirNext);
     res = difRange->SetEnd(range->GetEndParent(), range->EndOffset());
     nsresult tmp = difRange->SetStart(focusNode, focusOffset);
     if (NS_FAILED(tmp)) {
@@ -5182,7 +5184,7 @@ Selection::Extend(nsINode& aParentNode, uint32_t aOffset, ErrorResult& aRv)
   }
   else if (result1 == 0 && result3 > 0){//2, a1
     //select from 2 to 1a
-    dir = eDirPrevious;
+    SetDirection(eDirPrevious);
     range->SetStart(aParentNode, aOffset, aRv);
     if (aRv.Failed()) {
       return;
@@ -5227,7 +5229,7 @@ Selection::Extend(nsINode& aParentNode, uint32_t aOffset, ErrorResult& aRv)
         return;
       }
     }
-    dir = eDirNext;
+    SetDirection(eDirNext);
     range->SetEnd(aParentNode, aOffset, aRv);
     if (aRv.Failed()) {
       return;
@@ -5272,7 +5274,7 @@ Selection::Extend(nsINode& aParentNode, uint32_t aOffset, ErrorResult& aRv)
       aRv.Throw(res);
       return;
     }
-    dir = eDirPrevious;
+    SetDirection(eDirPrevious);
     range->SetStart(aParentNode, aOffset, aRv);
     if (aRv.Failed()) {
       return;
@@ -5291,7 +5293,7 @@ Selection::Extend(nsINode& aParentNode, uint32_t aOffset, ErrorResult& aRv)
     if (GetDirection() == eDirNext){
       range->SetEnd(startNode, startOffset);
     }
-    dir = eDirPrevious;
+    SetDirection(eDirPrevious);
     range->SetStart(aParentNode, aOffset, aRv);
     if (aRv.Failed()) {
       return;
@@ -5330,7 +5332,7 @@ Selection::Extend(nsINode& aParentNode, uint32_t aOffset, ErrorResult& aRv)
     if (aRv.Failed()) {
       return;
     }
-    dir = eDirPrevious;
+    SetDirection(eDirPrevious);
     res = difRange->SetEnd(focusNode, focusOffset);
     nsresult tmp = difRange->SetStart(range->GetStartParent(), range->StartOffset());
     if (NS_FAILED(tmp)) {
@@ -5359,15 +5361,11 @@ Selection::Extend(nsINode& aParentNode, uint32_t aOffset, ErrorResult& aRv)
 
   DEBUG_OUT_RANGE(range);
 #ifdef DEBUG_SELECTION
-  if (eDirNext == mDirection)
-    printf("    direction = 1  LEFT TO RIGHT\n");
-  else
-    printf("    direction = 0  RIGHT TO LEFT\n");
-#endif
-  SetDirection(dir);
-#ifdef DEBUG_SELECTION
+  if (GetDirection() != oldDirection) {
+    printf("    direction changed to %s\n",
+           GetDirection() == eDirNext? "eDirNext":"eDirPrevious");
+  }
   nsCOMPtr content = do_QueryInterface(&aParentNode);
-
   printf ("Sel. Extend to %p %s %d\n", content.get(),
           nsAtomCString(content->Tag()).get(), aOffset);
 #endif

From 04da2350f4c49b609baeda2d7e81a6d6baab3ee4 Mon Sep 17 00:00:00 2001
From: Mats Palmgren 
Date: Sat, 21 Feb 2015 04:27:59 +0000
Subject: [PATCH 11/48] Bug 1128722 - part 2, tests.

---
 .../tests/multi-range-user-select-ref.html    |  8 +++++
 .../base/tests/multi-range-user-select.html   | 31 +++++++++++++++++++
 .../base/tests/test_reftests_with_caret.html  |  4 +++
 3 files changed, 43 insertions(+)

diff --git a/layout/base/tests/multi-range-user-select-ref.html b/layout/base/tests/multi-range-user-select-ref.html
index b50ffc65071..812bae04791 100644
--- a/layout/base/tests/multi-range-user-select-ref.html
+++ b/layout/base/tests/multi-range-user-select-ref.html
@@ -107,6 +107,10 @@ function runTest() {
       } else {
         addChildRanges([[0,160,0,170]], e);
       }
+    } else if (test == "#prev8") {
+      if (op == "AD") {
+        addChildRanges([[0,150,0,155], [0,68,0,70]], e);
+      }
     }
   } else {
     if (test == "#next1") {
@@ -145,6 +149,10 @@ function runTest() {
       } else {
         sel.extend(e.firstChild, 170);
       }
+    } else if (test == "#next8") {
+      if (op == "AD") {
+        addChildRanges([[0,68,0,70], [0,150,0,155]], e);
+      }
     }
   }
   document.documentElement.removeAttribute("class");
diff --git a/layout/base/tests/multi-range-user-select.html b/layout/base/tests/multi-range-user-select.html
index 89814abbd7e..936df758300 100644
--- a/layout/base/tests/multi-range-user-select.html
+++ b/layout/base/tests/multi-range-user-select.html
@@ -125,6 +125,21 @@ function runTest() {
         action(e, 500, 125);
         checkRanges([[6,0,6,10]], e);
       }
+    } else if (test == "#prev8") {
+      if (action == accelDragSelect) {
+        sel.removeAllRanges();
+        var e = document.querySelector('#select');
+        synthesizeMouse(e, 200, 125, {type: "mousedown", accelKey: true});
+        synthesizeMouse(e, 200, 120, {type: "mousemove", accelKey: true});
+        synthesizeMouse(e, 200, 100, {type: "mousemove", accelKey: true});
+        synthesizeMouse(e, 200, 80, {type: "mousemove", accelKey: true});
+        synthesizeMouse(e, 210, 60, {type: "mousemove", accelKey: true});
+        synthesizeMouse(e, 200, 60, {type: "mousemove", accelKey: true});
+        synthesizeMouse(e, 200, 60, {type: "mouseup", accelKey: true});
+        var x3t = e.childNodes[3].firstChild;
+        var x5 = e.childNodes[5];
+        checkRanges([[x3t,3,-1,4], [x5,0,x5.firstChild,5]], e);
+      }
     }
   } else {
     if (test == "#next1") {
@@ -185,6 +200,22 @@ function runTest() {
         var r = sel.getRangeAt(2);
         checkRangePoints(r, [e.childNodes[5],0,e.childNodes[6],10], e);
       }
+    } else if (test == "#next8") {
+      if (action == accelDragSelect) {
+        sel.removeAllRanges();
+        var e = document.querySelector('#select');
+        synthesizeMouse(e, 200, 60, {type: "mousedown", accelKey: true});
+        synthesizeMouse(e, 180, 60, {type: "mousemove", accelKey: true});
+        synthesizeMouse(e, 200, 80, {type: "mousemove", accelKey: true});
+        synthesizeMouse(e, 200, 100, {type: "mousemove", accelKey: true});
+        synthesizeMouse(e, 200, 120, {type: "mousemove", accelKey: true});
+        synthesizeMouse(e, 190, 125, {type: "mousemove", accelKey: true});
+        synthesizeMouse(e, 200, 125, {type: "mousemove", accelKey: true});
+        synthesizeMouse(e, 200, 125, {type: "mouseup", accelKey: true});
+        var x3t = e.childNodes[3].firstChild;
+        var x5 = e.childNodes[5];
+        checkRanges([[x3t,3,-1,4], [x5,0,x5.firstChild,5]], e);
+      }
     }
   }
   document.documentElement.removeAttribute("class");
diff --git a/layout/base/tests/test_reftests_with_caret.html b/layout/base/tests/test_reftests_with_caret.html
index 3672c654078..a3e14966fea 100644
--- a/layout/base/tests/test_reftests_with_caret.html
+++ b/layout/base/tests/test_reftests_with_caret.html
@@ -194,6 +194,8 @@ if (navigator.platform.indexOf("Linux") >= 0 &&
     // eDirPrevious, Accel+drag-select (adding an additional range)
     [ 'multi-range-user-select.html#prev1AD' , 'multi-range-user-select-ref.html#prev1AD'  ] ,
     [ 'multi-range-user-select.html#prev7AD' , 'multi-range-user-select-ref.html#prev7AD'  ] ,
+    // eDirPrevious, Accel+drag-select (bug 1128722)
+    [ 'multi-range-user-select.html#prev8AD' , 'multi-range-user-select-ref.html#prev8AD'  ] ,
     // eDirPrevious, VK_RIGHT / LEFT
     [ 'multi-range-user-select.html#prev1SR' , 'multi-range-user-select-ref.html#prev1SR'  ] ,
     [ 'multi-range-user-select.html#prev1SL' , 'multi-range-user-select-ref.html#prev1SL'  ] ,
@@ -216,6 +218,8 @@ if (navigator.platform.indexOf("Linux") >= 0 &&
     // eDirNext, Accel+drag-select (adding an additional range)
     [ 'multi-range-user-select.html#next1AD' , 'multi-range-user-select-ref.html#next1AD'  ] ,
     [ 'multi-range-user-select.html#next7AD' , 'multi-range-user-select-ref.html#next7AD'  ] ,
+    // eDirNext, Accel+drag-select (bug 1128722)
+    [ 'multi-range-user-select.html#next8AD' , 'multi-range-user-select-ref.html#next8AD'  ] ,
     // eDirNext, VK_RIGHT / LEFT
     [ 'multi-range-user-select.html#next1SR' , 'multi-range-user-select-ref.html#next1SR'  ] ,
     [ 'multi-range-user-select.html#next1SL' , 'multi-range-user-select-ref.html#next1SL'  ] ,

From b1e1e53e87ca3d44b0c465421ca4ae7e84bb515d Mon Sep 17 00:00:00 2001
From: Boris Zbarsky 
Date: Fri, 20 Feb 2015 23:58:36 -0500
Subject: [PATCH 12/48] Bug 742194 part 1.  Add support for throwing
 uncatchable exceptions to Web IDL bindings.  People keep asking for this. 
 r=khuey

---
 dom/bindings/BindingUtils.cpp   | 3 +++
 dom/bindings/BindingUtils.h     | 7 +++++++
 dom/bindings/ErrorResult.h      | 8 ++++++++
 dom/bindings/Exceptions.cpp     | 8 ++++++++
 dom/bindings/ToJSValue.cpp      | 2 ++
 js/xpconnect/src/XPCThrower.cpp | 5 ++++-
 xpcom/base/ErrorList.h          | 3 +++
 7 files changed, 35 insertions(+), 1 deletion(-)

diff --git a/dom/bindings/BindingUtils.cpp b/dom/bindings/BindingUtils.cpp
index da8671176d3..5da10bc8b89 100644
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -2622,6 +2622,9 @@ ConvertExceptionToPromise(JSContext* cx,
 
   JS::Rooted exn(cx);
   if (!JS_GetPendingException(cx, &exn)) {
+    // This is very important: if there is no pending exception here but we're
+    // ending up in this code, that means the callee threw an uncatchable
+    // exception.  Just propagate that out as-is.
     return false;
   }
 
diff --git a/dom/bindings/BindingUtils.h b/dom/bindings/BindingUtils.h
index efe9f05f34a..639622c5f63 100644
--- a/dom/bindings/BindingUtils.h
+++ b/dom/bindings/BindingUtils.h
@@ -103,6 +103,13 @@ ThrowMethodFailedWithDetails(JSContext* cx, ErrorResult& rv,
                              const char* memberName,
                              bool reportJSContentExceptions = false)
 {
+  if (rv.IsUncatchableException()) {
+    // Nuke any existing exception on aCx, to make sure we're uncatchable.
+    JS_ClearPendingException(cx);
+    // Don't do any reporting.  Just return false, to create an
+    // uncatchable exception.
+    return false;
+  }
   if (rv.IsErrorWithMessage()) {
     rv.ReportErrorWithMessage(cx);
     return false;
diff --git a/dom/bindings/ErrorResult.h b/dom/bindings/ErrorResult.h
index 15b8399e5ae..26f64ae3dbb 100644
--- a/dom/bindings/ErrorResult.h
+++ b/dom/bindings/ErrorResult.h
@@ -97,6 +97,14 @@ public:
                                 const char* memberName);
   bool IsNotEnoughArgsError() const { return ErrorCode() == NS_ERROR_XPC_NOT_ENOUGH_ARGS; }
 
+  // Support for uncatchable exceptions.
+  void ThrowUncatchableException() {
+    Throw(NS_ERROR_UNCATCHABLE_EXCEPTION);
+  }
+  bool IsUncatchableException() const {
+    return ErrorCode() == NS_ERROR_UNCATCHABLE_EXCEPTION;
+  }
+
   // StealJSException steals the JS Exception from the object. This method must
   // be called only if IsJSException() returns true. This method also resets the
   // ErrorCode() to NS_OK.  The value will be ensured to be sanitized wrt to the
diff --git a/dom/bindings/Exceptions.cpp b/dom/bindings/Exceptions.cpp
index f228570d972..6e59f9168e9 100644
--- a/dom/bindings/Exceptions.cpp
+++ b/dom/bindings/Exceptions.cpp
@@ -88,6 +88,12 @@ ThrowExceptionObject(JSContext* aCx, Exception* aException)
 bool
 Throw(JSContext* aCx, nsresult aRv, const char* aMessage)
 {
+  if (aRv == NS_ERROR_UNCATCHABLE_EXCEPTION) {
+    // Nuke any existing exception on aCx, to make sure we're uncatchable.
+    JS_ClearPendingException(aCx);
+    return false;
+  }
+
   if (JS_IsExceptionPending(aCx)) {
     // Don't clobber the existing exception.
     return false;
@@ -128,6 +134,8 @@ Throw(JSContext* aCx, nsresult aRv, const char* aMessage)
 void
 ThrowAndReport(nsPIDOMWindow* aWindow, nsresult aRv, const char* aMessage)
 {
+  MOZ_ASSERT(aRv != NS_ERROR_UNCATCHABLE_EXCEPTION,
+             "Doesn't make sense to report uncatchable exceptions!");
   AutoJSAPI jsapi;
   if (NS_WARN_IF(!jsapi.InitWithLegacyErrorReporting(aWindow))) {
     return;
diff --git a/dom/bindings/ToJSValue.cpp b/dom/bindings/ToJSValue.cpp
index 9911d95fefa..458f9035e70 100644
--- a/dom/bindings/ToJSValue.cpp
+++ b/dom/bindings/ToJSValue.cpp
@@ -66,6 +66,8 @@ ToJSValue(JSContext* aCx,
           JS::MutableHandle aValue)
 {
   MOZ_ASSERT(aArgument.Failed());
+  MOZ_ASSERT(!aArgument.IsUncatchableException(),
+             "Doesn't make sense to convert uncatchable exception to a JS value!");
   AutoForceSetExceptionOnContext forceExn(aCx);
   DebugOnly throwResult = ThrowMethodFailedWithDetails(aCx, aArgument, "", "");
   MOZ_ASSERT(!throwResult);
diff --git a/js/xpconnect/src/XPCThrower.cpp b/js/xpconnect/src/XPCThrower.cpp
index 4e6c6df4224..7e36e7bd159 100644
--- a/js/xpconnect/src/XPCThrower.cpp
+++ b/js/xpconnect/src/XPCThrower.cpp
@@ -100,7 +100,10 @@ XPCThrower::ThrowBadResult(nsresult rv, nsresult result, XPCCallContext& ccx)
     *  If there is a pending exception when the native call returns and
     *  it has the same error result as returned by the native call, then
     *  the native call may be passing through an error from a previous JS
-    *  call. So we'll just throw that exception into our JS.
+    *  call. So we'll just throw that exception into our JS.  Note that
+    *  we don't need to worry about NS_ERROR_UNCATCHABLE_EXCEPTION,
+    *  because presumably there would be no pending exception for that
+    *  nsresult!
     */
 
     if (CheckForPendingException(result, ccx))
diff --git a/xpcom/base/ErrorList.h b/xpcom/base/ErrorList.h
index cf50c803946..5eef3550c1e 100644
--- a/xpcom/base/ErrorList.h
+++ b/xpcom/base/ErrorList.h
@@ -519,6 +519,9 @@
   ERROR(NS_ERROR_DOM_QUOTA_REACHED,                FAILURE(1014)),
   ERROR(NS_ERROR_DOM_JS_EXCEPTION,                 FAILURE(1015)),
 
+  /* A way to represent uncatchable exceptions */
+  ERROR(NS_ERROR_UNCATCHABLE_EXCEPTION,            FAILURE(1016)),
+
   /* May be used to indicate when e.g. setting a property value didn't
    * actually change the value, like for obj.foo = "bar"; obj.foo = "bar";
    * the second assignment throws NS_SUCCESS_DOM_NO_OPERATION.

From b7e936ba13033c955feee6ce6c79c74bef047e43 Mon Sep 17 00:00:00 2001
From: Boris Zbarsky 
Date: Fri, 20 Feb 2015 23:58:40 -0500
Subject: [PATCH 13/48] Bug 742194 part 2.  Use the new uncatchable exception
 machinery in worker XHR code.  r=khuey

---
 dom/workers/XMLHttpRequest.cpp | 33 +++++++++++++++------------------
 1 file changed, 15 insertions(+), 18 deletions(-)

diff --git a/dom/workers/XMLHttpRequest.cpp b/dom/workers/XMLHttpRequest.cpp
index 1bae6bd19eb..e2b9b625e5d 100644
--- a/dom/workers/XMLHttpRequest.cpp
+++ b/dom/workers/XMLHttpRequest.cpp
@@ -32,9 +32,6 @@ using namespace mozilla;
 using namespace mozilla::dom;
 USING_WORKERS_NAMESPACE
 
-// XXX Need to figure this out...
-#define UNCATCHABLE_EXCEPTION NS_ERROR_OUT_OF_MEMORY
-
 /**
  *  XMLHttpRequest in workers
  *
@@ -1907,7 +1904,7 @@ XMLHttpRequest::Open(const nsACString& aMethod, const nsAString& aUrl,
   mWorkerPrivate->AssertIsOnWorkerThread();
 
   if (mCanceled) {
-    aRv.Throw(UNCATCHABLE_EXCEPTION);
+    aRv.ThrowUncatchableException();
     return;
   }
 
@@ -1944,7 +1941,7 @@ XMLHttpRequest::SetRequestHeader(const nsACString& aHeader,
   mWorkerPrivate->AssertIsOnWorkerThread();
 
   if (mCanceled) {
-    aRv.Throw(UNCATCHABLE_EXCEPTION);
+    aRv.ThrowUncatchableException();
     return;
   }
 
@@ -1967,7 +1964,7 @@ XMLHttpRequest::SetTimeout(uint32_t aTimeout, ErrorResult& aRv)
   mWorkerPrivate->AssertIsOnWorkerThread();
 
   if (mCanceled) {
-    aRv.Throw(UNCATCHABLE_EXCEPTION);
+    aRv.ThrowUncatchableException();
     return;
   }
 
@@ -1993,7 +1990,7 @@ XMLHttpRequest::SetWithCredentials(bool aWithCredentials, ErrorResult& aRv)
   mWorkerPrivate->AssertIsOnWorkerThread();
 
   if (mCanceled) {
-    aRv.Throw(UNCATCHABLE_EXCEPTION);
+    aRv.ThrowUncatchableException();
     return;
   }
 
@@ -2020,7 +2017,7 @@ XMLHttpRequest::SetMozBackgroundRequest(bool aBackgroundRequest,
   mWorkerPrivate->AssertIsOnWorkerThread();
 
   if (mCanceled) {
-    aRv.Throw(UNCATCHABLE_EXCEPTION);
+    aRv.ThrowUncatchableException();
     return;
   }
 
@@ -2047,7 +2044,7 @@ XMLHttpRequest::GetUpload(ErrorResult& aRv)
   mWorkerPrivate->AssertIsOnWorkerThread();
 
   if (mCanceled) {
-    aRv.Throw(UNCATCHABLE_EXCEPTION);
+    aRv.ThrowUncatchableException();
     return nullptr;
   }
 
@@ -2069,7 +2066,7 @@ XMLHttpRequest::Send(ErrorResult& aRv)
   mWorkerPrivate->AssertIsOnWorkerThread();
 
   if (mCanceled) {
-    aRv.Throw(UNCATCHABLE_EXCEPTION);
+    aRv.ThrowUncatchableException();
     return;
   }
 
@@ -2091,7 +2088,7 @@ XMLHttpRequest::Send(const nsAString& aBody, ErrorResult& aRv)
   mWorkerPrivate->AssertIsOnWorkerThread();
 
   if (mCanceled) {
-    aRv.Throw(UNCATCHABLE_EXCEPTION);
+    aRv.ThrowUncatchableException();
     return;
   }
 
@@ -2117,7 +2114,7 @@ XMLHttpRequest::Send(JS::Handle aBody, ErrorResult& aRv)
   mWorkerPrivate->AssertIsOnWorkerThread();
 
   if (mCanceled) {
-    aRv.Throw(UNCATCHABLE_EXCEPTION);
+    aRv.ThrowUncatchableException();
     return;
   }
 
@@ -2163,7 +2160,7 @@ XMLHttpRequest::Send(File& aBody, ErrorResult& aRv)
   JSContext* cx = mWorkerPrivate->GetJSContext();
 
   if (mCanceled) {
-    aRv.Throw(UNCATCHABLE_EXCEPTION);
+    aRv.ThrowUncatchableException();
     return;
   }
 
@@ -2230,7 +2227,7 @@ XMLHttpRequest::Abort(ErrorResult& aRv)
   mWorkerPrivate->AssertIsOnWorkerThread();
 
   if (mCanceled) {
-    aRv.Throw(UNCATCHABLE_EXCEPTION);
+    aRv.ThrowUncatchableException();
   }
 
   if (!mProxy) {
@@ -2258,7 +2255,7 @@ XMLHttpRequest::GetResponseHeader(const nsACString& aHeader,
   mWorkerPrivate->AssertIsOnWorkerThread();
 
   if (mCanceled) {
-    aRv.Throw(UNCATCHABLE_EXCEPTION);
+    aRv.ThrowUncatchableException();
     return;
   }
 
@@ -2285,7 +2282,7 @@ XMLHttpRequest::GetAllResponseHeaders(nsACString& aResponseHeaders,
   mWorkerPrivate->AssertIsOnWorkerThread();
 
   if (mCanceled) {
-    aRv.Throw(UNCATCHABLE_EXCEPTION);
+    aRv.ThrowUncatchableException();
     return;
   }
 
@@ -2311,7 +2308,7 @@ XMLHttpRequest::OverrideMimeType(const nsAString& aMimeType, ErrorResult& aRv)
   mWorkerPrivate->AssertIsOnWorkerThread();
 
   if (mCanceled) {
-    aRv.Throw(UNCATCHABLE_EXCEPTION);
+    aRv.ThrowUncatchableException();
     return;
   }
 
@@ -2342,7 +2339,7 @@ XMLHttpRequest::SetResponseType(XMLHttpRequestResponseType aResponseType,
   mWorkerPrivate->AssertIsOnWorkerThread();
 
   if (mCanceled) {
-    aRv.Throw(UNCATCHABLE_EXCEPTION);
+    aRv.ThrowUncatchableException();
     return;
   }
 

From 5c310df1f747dfb844cca872b6e080b3e7cfaa9b Mon Sep 17 00:00:00 2001
From: James Willcox 
Date: Fri, 20 Feb 2015 13:52:21 -0600
Subject: [PATCH 14/48] Bug 1090300 - Repopulate input buffers when necessary
 in Android media decoder r=gcp

---
 .../fmp4/android/AndroidDecoderModule.cpp     | 29 +++++++++++++++++--
 dom/media/fmp4/android/AndroidDecoderModule.h |  1 +
 2 files changed, 28 insertions(+), 2 deletions(-)

diff --git a/dom/media/fmp4/android/AndroidDecoderModule.cpp b/dom/media/fmp4/android/AndroidDecoderModule.cpp
index 273fafc829f..5d9cf05e282 100644
--- a/dom/media/fmp4/android/AndroidDecoderModule.cpp
+++ b/dom/media/fmp4/android/AndroidDecoderModule.cpp
@@ -364,6 +364,29 @@ nsresult MediaCodecDataDecoder::InitDecoder(Surface::Param aSurface)
     break; \
   }
 
+nsresult MediaCodecDataDecoder::GetInputBuffer(JNIEnv* env, int index, jni::Object::LocalRef* buffer)
+{
+  bool retried = false;
+  while (!*buffer) {
+    *buffer = jni::Object::LocalRef::Adopt(env->GetObjectArrayElement(mInputBuffers.Get(), index));
+    if (!*buffer) {
+      if (!retried) {
+        // Reset the input buffers and then try again
+        nsresult res = ResetInputBuffers();
+        if (NS_FAILED(res)) {
+          return res;
+        }
+        retried = true;
+      } else {
+        // We already tried resetting the input buffers, return an error
+        return NS_ERROR_FAILURE;
+      }
+    }
+  }
+
+  return NS_OK;
+}
+
 void MediaCodecDataDecoder::DecoderLoop()
 {
   bool outputDone = false;
@@ -433,8 +456,10 @@ void MediaCodecDataDecoder::DecoderLoop()
       HANDLE_DECODER_ERROR();
 
       if (inputIndex >= 0) {
-        auto buffer = jni::Object::LocalRef::Adopt(
-            frame.GetEnv()->GetObjectArrayElement(mInputBuffers.Get(), inputIndex));
+        jni::Object::LocalRef buffer(frame.GetEnv());
+        res = GetInputBuffer(frame.GetEnv(), inputIndex, &buffer);
+        HANDLE_DECODER_ERROR();
+
         void* directBuffer = frame.GetEnv()->GetDirectBufferAddress(buffer.Get());
 
         MOZ_ASSERT(frame.GetEnv()->GetDirectBufferCapacity(buffer.Get()) >= sample->size,
diff --git a/dom/media/fmp4/android/AndroidDecoderModule.h b/dom/media/fmp4/android/AndroidDecoderModule.h
index 1d6c9c31200..a0d961d6829 100644
--- a/dom/media/fmp4/android/AndroidDecoderModule.h
+++ b/dom/media/fmp4/android/AndroidDecoderModule.h
@@ -92,6 +92,7 @@ protected:
   nsresult ResetOutputBuffers();
 
   void DecoderLoop();
+  nsresult GetInputBuffer(JNIEnv* env, int index, jni::Object::LocalRef* buffer);
   virtual void ClearQueue();
 };
 

From 2624adf3e5d824691730d42b86f3dd9eccccf964 Mon Sep 17 00:00:00 2001
From: Daniel Holbert 
Date: Fri, 20 Feb 2015 22:31:09 -0800
Subject: [PATCH 15/48] Bug 1135181: Remove unnecessary 'nsresult' return value
 from nsCSSRuleProcessor::Startup(), since it always succeeds. r=heycam

---
 layout/build/nsLayoutStatics.cpp    | 6 +-----
 layout/style/nsCSSRuleProcessor.cpp | 4 +---
 layout/style/nsCSSRuleProcessor.h   | 2 +-
 3 files changed, 3 insertions(+), 9 deletions(-)

diff --git a/layout/build/nsLayoutStatics.cpp b/layout/build/nsLayoutStatics.cpp
index 0c13c515d3e..eb394614415 100644
--- a/layout/build/nsLayoutStatics.cpp
+++ b/layout/build/nsLayoutStatics.cpp
@@ -242,11 +242,7 @@ nsLayoutStatics::Initialize()
     return rv;
   }
 
-  rv = nsCSSRuleProcessor::Startup();
-  if (NS_FAILED(rv)) {
-    NS_ERROR("Could not initialize nsCSSRuleProcessor");
-    return rv;
-  }
+  nsCSSRuleProcessor::Startup();
 
 #ifdef MOZ_XUL
   rv = nsXULPopupManager::Init();
diff --git a/layout/style/nsCSSRuleProcessor.cpp b/layout/style/nsCSSRuleProcessor.cpp
index cda146e122e..66ebbd0e31b 100644
--- a/layout/style/nsCSSRuleProcessor.cpp
+++ b/layout/style/nsCSSRuleProcessor.cpp
@@ -1101,13 +1101,11 @@ nsCSSRuleProcessor::ClearSheets()
   mSheets.Clear();
 }
 
-/* static */ nsresult
+/* static */ void
 nsCSSRuleProcessor::Startup()
 {
   Preferences::AddBoolVarCache(&gSupportVisitedPseudo, VISITED_PSEUDO_PREF,
                                true);
-
-  return NS_OK;
 }
 
 static bool
diff --git a/layout/style/nsCSSRuleProcessor.h b/layout/style/nsCSSRuleProcessor.h
index b683e392bbe..c5bcca29d4e 100644
--- a/layout/style/nsCSSRuleProcessor.h
+++ b/layout/style/nsCSSRuleProcessor.h
@@ -65,7 +65,7 @@ public:
 public:
   nsresult ClearRuleCascades();
 
-  static nsresult Startup();
+  static void Startup();
   static void Shutdown();
   static void FreeSystemMetrics();
   static bool HasSystemMetric(nsIAtom* aMetric);

From 43f23d98e1fcade082aac2f725779263aef9babb Mon Sep 17 00:00:00 2001
From: Masatoshi Kimura 
Date: Sat, 21 Feb 2015 17:20:22 +0900
Subject: [PATCH 16/48] Bug 1127339 - Detect SSLv3-only server in PSM. r=keeler

---
 security/manager/ssl/src/nsNSSIOLayer.cpp | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/security/manager/ssl/src/nsNSSIOLayer.cpp b/security/manager/ssl/src/nsNSSIOLayer.cpp
index 01a9d8aa50f..9b6c9264178 100644
--- a/security/manager/ssl/src/nsNSSIOLayer.cpp
+++ b/security/manager/ssl/src/nsNSSIOLayer.cpp
@@ -1209,6 +1209,12 @@ retryDueToTLSIntolerance(PRErrorCode err, nsNSSSocketInfo* socketInfo)
   SSLVersionRange range = socketInfo->GetTLSVersionRange();
   nsSSLIOLayerHelpers& helpers = socketInfo->SharedState().IOLayerHelpers();
 
+  if (err == SSL_ERROR_UNSUPPORTED_VERSION &&
+      range.min == SSL_LIBRARY_VERSION_TLS_1_0) {
+    socketInfo->SetSecurityState(nsIWebProgressListener::STATE_IS_INSECURE |
+                                 nsIWebProgressListener::STATE_USES_SSL_3);
+  }
+
   if (err == SSL_ERROR_INAPPROPRIATE_FALLBACK_ALERT) {
     // This is a clear signal that we've fallen back too many versions.  Treat
     // this as a hard failure, but forget any intolerance so that later attempts

From c584cd0cbb52f759e6ce1baa6da8b9ebc74e0a25 Mon Sep 17 00:00:00 2001
From: Masatoshi Kimura 
Date: Sat, 21 Feb 2015 17:20:22 +0900
Subject: [PATCH 17/48] Bug 1127339 - Assign a dedicated error code for SSLv3
 in docshell. r=bz

---
 docshell/base/nsDocShell.cpp                   | 11 +++++++++--
 dom/locales/en-US/chrome/appstrings.properties |  1 +
 2 files changed, 10 insertions(+), 2 deletions(-)

diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp
index 48e9612f457..c396eaa1290 100644
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -4975,8 +4975,15 @@ nsDocShell::DisplayLoadError(nsresult aError, nsIURI* aURI,
     }
     tsi = do_QueryInterface(securityInfo);
     if (tsi) {
-      // Usually we should have aFailedChannel and get a detailed message
-      tsi->GetErrorMessage(getter_Copies(messageStr));
+      uint32_t securityState;
+      tsi->GetSecurityState(&securityState);
+      if (securityState & nsIWebProgressListener::STATE_USES_SSL_3) {
+        error.AssignLiteral("sslv3Used");
+        addHostPort = true;
+      } else {
+        // Usually we should have aFailedChannel and get a detailed message
+        tsi->GetErrorMessage(getter_Copies(messageStr));
+      }
     } else {
       // No channel, let's obtain the generic error message
       if (nsserr) {
diff --git a/dom/locales/en-US/chrome/appstrings.properties b/dom/locales/en-US/chrome/appstrings.properties
index 74d002cdfcd..3ee5ee7006f 100644
--- a/dom/locales/en-US/chrome/appstrings.properties
+++ b/dom/locales/en-US/chrome/appstrings.properties
@@ -33,3 +33,4 @@ phishingBlocked=The website at %S has been reported as a web forgery designed to
 cspBlocked=This page has a content security policy that prevents it from being loaded in this way.
 corruptedContentError=The page you are trying to view cannot be shown because an error in the data transmission was detected.
 remoteXUL=This page uses an unsupported technology that is no longer available by default.
+sslv3Used=The safety of your data on %S could not be guaranteed because it uses SSLv3, a broken security protocol.

From 8f99dc2e8577e98049c9404d283bd597b3a8da02 Mon Sep 17 00:00:00 2001
From: Masatoshi Kimura 
Date: Sat, 21 Feb 2015 17:20:23 +0900
Subject: [PATCH 18/48] Bug 1127339 - Move SSLv3 detection out of
 aboutNetError.xhtml. r=gijs

---
 .../chrome/overrides/appstrings.properties    |  1 +
 browser/base/content/aboutNetError.xhtml      | 27 +++++--------------
 .../chrome/overrides/appstrings.properties    |  2 ++
 .../en-US/chrome/overrides/netError.dtd       | 13 ++++-----
 .../en-US/overrides/appstrings.properties     |  1 +
 .../webapprt/overrides/appstrings.properties  |  1 +
 6 files changed, 16 insertions(+), 29 deletions(-)

diff --git a/b2g/locales/en-US/chrome/overrides/appstrings.properties b/b2g/locales/en-US/chrome/overrides/appstrings.properties
index 77f2018bef9..805a725527f 100644
--- a/b2g/locales/en-US/chrome/overrides/appstrings.properties
+++ b/b2g/locales/en-US/chrome/overrides/appstrings.properties
@@ -34,3 +34,4 @@ phishingBlocked=The website at %S has been reported as a web forgery designed to
 cspBlocked=This page has a content security policy that prevents it from being loaded in this way.
 corruptedContentError=The page you are trying to view cannot be shown because an error in the data transmission was detected.
 remoteXUL=This page uses an unsupported technology that is no longer available by default in Firefox.
+sslv3Used=Firefox cannot guarantee the safety of your data on %S because it uses SSLv3, a broken security protocol.
diff --git a/browser/base/content/aboutNetError.xhtml b/browser/base/content/aboutNetError.xhtml
index d085b256b26..5a7bd1879b1 100644
--- a/browser/base/content/aboutNetError.xhtml
+++ b/browser/base/content/aboutNetError.xhtml
@@ -156,24 +156,10 @@
           errDesc.id = "errorLongDesc";
         }
 
-        if (err == "nssFailure2" &&
-            sd.textContent.contains("ssl_error_unsupported_version")) {
-          var ssl3ErrorTitle = document.getElementById("et_ssl3");
-          var ssl3ErrorDesc = document.getElementById("ed_ssl3");
-          var ssl3ShortDesc = document.getElementById("esd_ssl3");
+        if (err == "sslv3Used") {
           var learnMoreText = document.getElementById("learn_more_ssl3");
 
-          errTitle.parentNode.replaceChild(ssl3ErrorTitle, errTitle);
-          ssl3ErrorTitle.id = "errorTitleText";
-          ssl3ErrorTitle.setAttribute("sslv3", "true");
-          errTitle = ssl3ErrorTitle;
-
-          sd.innerHTML = ssl3ShortDesc.innerHTML;
-          sd.querySelector('span').textContent = location.hostname;
-
-          errDesc.parentNode.replaceChild(ssl3ErrorDesc, errDesc);
-          ssl3ErrorDesc.id = "errorLongDesc";
-          ssl3ErrorDesc.querySelector('span').textContent = "ssl_error_unsupported_version";
+          errTitle.setAttribute("sslv3", "true");
 
           var retryBtn = document.getElementById("errorTryAgain");
           retryBtn.textContent = learnMoreText.textContent;
@@ -218,7 +204,7 @@
 
         window.addEventListener("AboutNetErrorOptions", function(evt) {
         // Pinning errors are of type nssFailure2
-          if (getErrorCode() == "nssFailure2" && !errTitle.hasAttribute("sslv3")) {
+          if (getErrorCode() == "nssFailure2") {
             var learnMoreLink = document.getElementById("learnMoreLink");
             // nssFailure2 also gets us other non-overrideable errors. Choose
             // a "learn more" link based on description:
@@ -391,7 +377,7 @@
         

&cspBlocked.title;

&remoteXUL.title;

&corruptedContentError.title;

-

&oldSecurityProtocol.title;

+

&sslv3Used.title;

&generic.longDesc;
@@ -418,9 +404,8 @@
&cspBlocked.longDesc;
&remoteXUL.longDesc;
&corruptedContentError.longDesc;
-
&oldSecurityProtocol.longDesc2;
-
&oldSecurityProtocol.advancedInfo;
-
&oldSecurityProtocol.learnMore;
+
&sslv3Used.longDesc;
+
&sslv3Used.learnMore;
diff --git a/browser/locales/en-US/chrome/overrides/appstrings.properties b/browser/locales/en-US/chrome/overrides/appstrings.properties index 77f2018bef9..cc9185a4aab 100644 --- a/browser/locales/en-US/chrome/overrides/appstrings.properties +++ b/browser/locales/en-US/chrome/overrides/appstrings.properties @@ -34,3 +34,5 @@ phishingBlocked=The website at %S has been reported as a web forgery designed to cspBlocked=This page has a content security policy that prevents it from being loaded in this way. corruptedContentError=The page you are trying to view cannot be shown because an error in the data transmission was detected. remoteXUL=This page uses an unsupported technology that is no longer available by default in Firefox. +## LOCALIZATION NOTE (oldSecurityProtocol) - Do not translate "%S". +sslv3Used=Firefox cannot guarantee the safety of your data on %S because it uses SSLv3, a broken security protocol. diff --git a/browser/locales/en-US/chrome/overrides/netError.dtd b/browser/locales/en-US/chrome/overrides/netError.dtd index f8711c5aae5..807b5b26507 100644 --- a/browser/locales/en-US/chrome/overrides/netError.dtd +++ b/browser/locales/en-US/chrome/overrides/netError.dtd @@ -205,11 +205,8 @@ functionality specific to firefox. -->
  • Please contact the website owners to inform them of this problem.

"> - - - because it uses SSLv3, a broken security protocol."> - -"> - + + + + diff --git a/mobile/locales/en-US/overrides/appstrings.properties b/mobile/locales/en-US/overrides/appstrings.properties index 77f2018bef9..805a725527f 100644 --- a/mobile/locales/en-US/overrides/appstrings.properties +++ b/mobile/locales/en-US/overrides/appstrings.properties @@ -34,3 +34,4 @@ phishingBlocked=The website at %S has been reported as a web forgery designed to cspBlocked=This page has a content security policy that prevents it from being loaded in this way. corruptedContentError=The page you are trying to view cannot be shown because an error in the data transmission was detected. remoteXUL=This page uses an unsupported technology that is no longer available by default in Firefox. +sslv3Used=Firefox cannot guarantee the safety of your data on %S because it uses SSLv3, a broken security protocol. diff --git a/webapprt/locales/en-US/webapprt/overrides/appstrings.properties b/webapprt/locales/en-US/webapprt/overrides/appstrings.properties index 53549cdf9a9..b41d7ac26f0 100644 --- a/webapprt/locales/en-US/webapprt/overrides/appstrings.properties +++ b/webapprt/locales/en-US/webapprt/overrides/appstrings.properties @@ -33,3 +33,4 @@ phishingBlocked=The website at %S has been reported as a web forgery designed to cspBlocked=This application tried to access a resource that has a content security policy that prevents it from being loaded in this way. corruptedContentError=The application cannot continue loading because an error in the data transmission was detected. remoteXUL=This application tried to use an unsupported technology that is no longer available. +sslv3Used=This application cannot guarantee the safety of your data on %S because it uses SSLv3, a broken security protocol. From a18446ea1bedde145978ee0d3e56f844db5b2d7a Mon Sep 17 00:00:00 2001 From: Xidorn Quan Date: Sun, 22 Feb 2015 00:39:33 +1300 Subject: [PATCH 19/48] Bug 1134432 part 1 - Suppress break inside ruby when calculating intrinsic isize. r=dbaron Breaks during reflow have been suppressed in bug 1098272. --- layout/generic/nsBRFrame.cpp | 8 ++++++-- layout/generic/nsFrame.cpp | 1 + 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/layout/generic/nsBRFrame.cpp b/layout/generic/nsBRFrame.cpp index 8d519f28844..c5deaa907a0 100644 --- a/layout/generic/nsBRFrame.cpp +++ b/layout/generic/nsBRFrame.cpp @@ -164,14 +164,18 @@ BRFrame::Reflow(nsPresContext* aPresContext, BRFrame::AddInlineMinISize(nsRenderingContext *aRenderingContext, nsIFrame::InlineMinISizeData *aData) { - aData->ForceBreak(aRenderingContext); + if (!StyleContext()->IsInlineDescendantOfRuby()) { + aData->ForceBreak(aRenderingContext); + } } /* virtual */ void BRFrame::AddInlinePrefISize(nsRenderingContext *aRenderingContext, nsIFrame::InlinePrefISizeData *aData) { - aData->ForceBreak(aRenderingContext); + if (!StyleContext()->IsInlineDescendantOfRuby()) { + aData->ForceBreak(aRenderingContext); + } } /* virtual */ nscoord diff --git a/layout/generic/nsFrame.cpp b/layout/generic/nsFrame.cpp index e4c11b15489..50f52e32f11 100644 --- a/layout/generic/nsFrame.cpp +++ b/layout/generic/nsFrame.cpp @@ -3917,6 +3917,7 @@ nsFrame::AddInlineMinISize(nsRenderingContext *aRenderingContext, NS_ASSERTION(GetParent(), "Must have a parent if we get here!"); nsIFrame* parent = GetParent(); bool canBreak = !CanContinueTextRun() && + !parent->StyleContext()->IsInlineDescendantOfRuby() && parent->StyleText()->WhiteSpaceCanWrap(parent); if (canBreak) From 489efc605ecf1c847d8bbb5654da58cbac0a2a51 Mon Sep 17 00:00:00 2001 From: Xidorn Quan Date: Sun, 22 Feb 2015 00:39:33 +1300 Subject: [PATCH 20/48] Bug 1134432 part 2 - Fix basic inline min/pref isize calculation of ruby frames. r=dbaron --- layout/generic/nsRubyBaseContainerFrame.cpp | 54 +++++++++++++++------ layout/generic/nsRubyFrame.cpp | 18 ++++--- 2 files changed, 48 insertions(+), 24 deletions(-) diff --git a/layout/generic/nsRubyBaseContainerFrame.cpp b/layout/generic/nsRubyBaseContainerFrame.cpp index 93d40355679..394bde72a3d 100644 --- a/layout/generic/nsRubyBaseContainerFrame.cpp +++ b/layout/generic/nsRubyBaseContainerFrame.cpp @@ -209,15 +209,21 @@ CalculateColumnPrefISize(nsRenderingContext* aRenderingContext, for (uint32_t i = 0; i < levelCount; i++) { nsIFrame* frame = aEnumerator.GetFrameAtLevel(i); if (frame) { - max = std::max(max, frame->GetPrefISize(aRenderingContext)); + nsIFrame::InlinePrefISizeData data; + frame->AddInlinePrefISize(aRenderingContext, &data); + MOZ_ASSERT(data.prevLines == 0, "Shouldn't have prev lines"); + max = std::max(max, data.currentLine); } } return max; } +// FIXME Currently we use pref isize of ruby content frames for +// computing min isize of ruby frame, which may cause problem. +// See bug 1134945. /* virtual */ void nsRubyBaseContainerFrame::AddInlineMinISize( - nsRenderingContext *aRenderingContext, nsIFrame::InlineMinISizeData *aData) + nsRenderingContext *aRenderingContext, nsIFrame::InlineMinISizeData *aData) { AutoTextContainerArray textContainers; GetTextContainers(textContainers); @@ -226,38 +232,54 @@ nsRubyBaseContainerFrame::AddInlineMinISize( if (textContainers[i]->IsSpanContainer()) { // Since spans are not breakable internally, use our pref isize // directly if there is any span. - aData->currentLine += GetPrefISize(aRenderingContext); + nsIFrame::InlinePrefISizeData data; + AddInlinePrefISize(aRenderingContext, &data); + aData->currentLine += data.currentLine; + if (data.currentLine > 0) { + aData->atStartOfLine = false; + } return; } } - nscoord max = 0; - RubyColumnEnumerator enumerator(this, textContainers); - for (; !enumerator.AtEnd(); enumerator.Next()) { - // We use *pref* isize for computing the min isize of columns - // because ruby bases and texts are unbreakable internally. - max = std::max(max, CalculateColumnPrefISize(aRenderingContext, - enumerator)); + for (nsIFrame* frame = this; frame; frame = frame->GetNextInFlow()) { + RubyColumnEnumerator enumerator( + static_cast(frame), textContainers); + for (; !enumerator.AtEnd(); enumerator.Next()) { + // FIXME This simply assumes that there is an optional break + // between ruby columns, which is no true in many cases. + aData->OptionallyBreak(aRenderingContext); + nscoord isize = CalculateColumnPrefISize(aRenderingContext, enumerator); + aData->currentLine += isize; + if (isize > 0) { + aData->atStartOfLine = false; + } + } } - aData->currentLine += max; } /* virtual */ void nsRubyBaseContainerFrame::AddInlinePrefISize( - nsRenderingContext *aRenderingContext, nsIFrame::InlinePrefISizeData *aData) + nsRenderingContext *aRenderingContext, nsIFrame::InlinePrefISizeData *aData) { AutoTextContainerArray textContainers; GetTextContainers(textContainers); nscoord sum = 0; - RubyColumnEnumerator enumerator(this, textContainers); - for (; !enumerator.AtEnd(); enumerator.Next()) { - sum += CalculateColumnPrefISize(aRenderingContext, enumerator); + for (nsIFrame* frame = this; frame; frame = frame->GetNextInFlow()) { + RubyColumnEnumerator enumerator( + static_cast(frame), textContainers); + for (; !enumerator.AtEnd(); enumerator.Next()) { + sum += CalculateColumnPrefISize(aRenderingContext, enumerator); + } } for (uint32_t i = 0, iend = textContainers.Length(); i < iend; i++) { if (textContainers[i]->IsSpanContainer()) { nsIFrame* frame = textContainers[i]->GetFirstPrincipalChild(); - sum = std::max(sum, frame->GetPrefISize(aRenderingContext)); + nsIFrame::InlinePrefISizeData data; + frame->AddInlinePrefISize(aRenderingContext, &data); + MOZ_ASSERT(data.prevLines == 0, "Shouldn't have prev lines"); + sum = std::max(sum, data.currentLine); } } aData->currentLine += sum; diff --git a/layout/generic/nsRubyFrame.cpp b/layout/generic/nsRubyFrame.cpp index d1b83a01ff0..4e5cfadad5e 100644 --- a/layout/generic/nsRubyFrame.cpp +++ b/layout/generic/nsRubyFrame.cpp @@ -106,22 +106,24 @@ SegmentEnumerator::Next() nsRubyFrame::AddInlineMinISize(nsRenderingContext *aRenderingContext, nsIFrame::InlineMinISizeData *aData) { - nscoord max = 0; - for (SegmentEnumerator e(this); !e.AtEnd(); e.Next()) { - max = std::max(max, e.GetBaseContainer()->GetMinISize(aRenderingContext)); + for (nsIFrame* frame = this; frame; frame = frame->GetNextInFlow()) { + for (SegmentEnumerator e(static_cast(frame)); + !e.AtEnd(); e.Next()) { + e.GetBaseContainer()->AddInlineMinISize(aRenderingContext, aData); + } } - aData->currentLine += max; } /* virtual */ void nsRubyFrame::AddInlinePrefISize(nsRenderingContext *aRenderingContext, nsIFrame::InlinePrefISizeData *aData) { - nscoord sum = 0; - for (SegmentEnumerator e(this); !e.AtEnd(); e.Next()) { - sum += e.GetBaseContainer()->GetPrefISize(aRenderingContext); + for (nsIFrame* frame = this; frame; frame = frame->GetNextInFlow()) { + for (SegmentEnumerator e(static_cast(frame)); + !e.AtEnd(); e.Next()) { + e.GetBaseContainer()->AddInlinePrefISize(aRenderingContext, aData); + } } - aData->currentLine += sum; } /* virtual */ void From f6fb4bc5481cb2afc2a2b5fb2973e77894a42cae Mon Sep 17 00:00:00 2001 From: Xidorn Quan Date: Sun, 22 Feb 2015 00:39:33 +1300 Subject: [PATCH 21/48] Bug 1134432 part 3 - Move a function upwards for reuse. r=dbaron --- layout/generic/nsRubyBaseContainerFrame.cpp | 86 ++++++++++----------- 1 file changed, 43 insertions(+), 43 deletions(-) diff --git a/layout/generic/nsRubyBaseContainerFrame.cpp b/layout/generic/nsRubyBaseContainerFrame.cpp index 394bde72a3d..330435efd4a 100644 --- a/layout/generic/nsRubyBaseContainerFrame.cpp +++ b/layout/generic/nsRubyBaseContainerFrame.cpp @@ -200,6 +200,49 @@ RubyColumnEnumerator::GetColumn(RubyColumn& aColumn) const aColumn.mIsIntraLevelWhitespace = mAtIntraLevelWhitespace; } +static gfxBreakPriority +LineBreakBefore(const nsHTMLReflowState& aReflowState, nsRubyBaseFrame* aFrame) +{ + for (nsIFrame* child = aFrame; child; + child = child->GetFirstPrincipalChild()) { + if (!child->CanContinueTextRun()) { + // It is not an inline element. We can break before it. + return gfxBreakPriority::eNormalBreak; + } + if (child->GetType() != nsGkAtoms::textFrame) { + continue; + } + + auto textFrame = static_cast(child); + gfxSkipCharsIterator iter = + textFrame->EnsureTextRun(nsTextFrame::eInflated, + aReflowState.rendContext->ThebesContext(), + aReflowState.mLineLayout->LineContainerFrame(), + aReflowState.mLineLayout->GetLine()); + iter.SetOriginalOffset(textFrame->GetContentOffset()); + uint32_t pos = iter.GetSkippedOffset(); + gfxTextRun* textRun = textFrame->GetTextRun(nsTextFrame::eInflated); + if (pos >= textRun->GetLength()) { + // The text frame contains no character at all. + return gfxBreakPriority::eNoBreak; + } + // Return whether we can break before the first character. + if (textRun->CanBreakLineBefore(pos)) { + return gfxBreakPriority::eNormalBreak; + } + // Check whether we can wrap word here. + const nsStyleText* textStyle = textFrame->StyleText(); + if (textStyle->WordCanWrap(textFrame) && textRun->IsClusterStart(pos)) { + return gfxBreakPriority::eWordWrapBreak; + } + // We cannot break before. + return gfxBreakPriority::eNoBreak; + } + // Neither block, nor text frame is found as a leaf. We won't break + // before this base frame. It is the behavior of empty spans. + return gfxBreakPriority::eNoBreak; +} + static nscoord CalculateColumnPrefISize(nsRenderingContext* aRenderingContext, const RubyColumnEnumerator& aEnumerator) @@ -623,49 +666,6 @@ nsRubyBaseContainerFrame::ReflowColumns(const ReflowState& aReflowState, return icoord; } -static gfxBreakPriority -LineBreakBefore(const nsHTMLReflowState& aReflowState, nsRubyBaseFrame* aFrame) -{ - for (nsIFrame* child = aFrame; child; - child = child->GetFirstPrincipalChild()) { - if (!child->CanContinueTextRun()) { - // It is not an inline element. We can break before it. - return gfxBreakPriority::eNormalBreak; - } - if (child->GetType() != nsGkAtoms::textFrame) { - continue; - } - - auto textFrame = static_cast(child); - gfxSkipCharsIterator iter = - textFrame->EnsureTextRun(nsTextFrame::eInflated, - aReflowState.rendContext->ThebesContext(), - aReflowState.mLineLayout->LineContainerFrame(), - aReflowState.mLineLayout->GetLine()); - iter.SetOriginalOffset(textFrame->GetContentOffset()); - uint32_t pos = iter.GetSkippedOffset(); - gfxTextRun* textRun = textFrame->GetTextRun(nsTextFrame::eInflated); - if (pos >= textRun->GetLength()) { - // The text frame contains no character at all. - return gfxBreakPriority::eNoBreak; - } - // Return whether we can break before the first character. - if (textRun->CanBreakLineBefore(pos)) { - return gfxBreakPriority::eNormalBreak; - } - // Check whether we can wrap word here. - const nsStyleText* textStyle = textFrame->StyleText(); - if (textStyle->WordCanWrap(textFrame) && textRun->IsClusterStart(pos)) { - return gfxBreakPriority::eWordWrapBreak; - } - // We cannot break before. - return gfxBreakPriority::eNoBreak; - } - // Neither block, nor text frame is found as a leaf. We won't break - // before this base frame. It is the behavior of empty spans. - return gfxBreakPriority::eNoBreak; -} - nscoord nsRubyBaseContainerFrame::ReflowOneColumn(const ReflowState& aReflowState, uint32_t aColumnIndex, From 99dd10e6b8385aad7f4412c5c441f95c308c7c7c Mon Sep 17 00:00:00 2001 From: Xidorn Quan Date: Sun, 22 Feb 2015 00:39:33 +1300 Subject: [PATCH 22/48] Bug 1134432 part 4 - Move line break checking code to an independent method for reuse. r=dbaron --- layout/generic/nsRubyBaseContainerFrame.cpp | 38 ++++++++++++++------- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/layout/generic/nsRubyBaseContainerFrame.cpp b/layout/generic/nsRubyBaseContainerFrame.cpp index 330435efd4a..df188de476c 100644 --- a/layout/generic/nsRubyBaseContainerFrame.cpp +++ b/layout/generic/nsRubyBaseContainerFrame.cpp @@ -243,6 +243,28 @@ LineBreakBefore(const nsHTMLReflowState& aReflowState, nsRubyBaseFrame* aFrame) return gfxBreakPriority::eNoBreak; } +static void +GetIsLineBreakAllowed(nsIFrame* aFrame, bool aIsLineBreakable, + bool* aAllowInitialLineBreak, bool* aAllowLineBreak) +{ + nsIFrame* parent = aFrame->GetParent(); + bool inNestedRuby = parent->StyleContext()->IsInlineDescendantOfRuby(); + // Allow line break between ruby bases when white-space allows, + // we are not inside a nested ruby, and there is no span. + bool allowLineBreak = !inNestedRuby && + aFrame->StyleText()->WhiteSpaceCanWrap(aFrame); + bool allowInitialLineBreak = allowLineBreak; + if (!aFrame->GetPrevInFlow()) { + allowInitialLineBreak = !inNestedRuby && + parent->StyleText()->WhiteSpaceCanWrap(parent); + } + if (!aIsLineBreakable) { + allowInitialLineBreak = false; + } + *aAllowInitialLineBreak = allowInitialLineBreak; + *aAllowLineBreak = allowLineBreak; +} + static nscoord CalculateColumnPrefISize(nsRenderingContext* aRenderingContext, const RubyColumnEnumerator& aEnumerator) @@ -464,19 +486,9 @@ nsRubyBaseContainerFrame::Reflow(nsPresContext* aPresContext, 0, aReflowState.AvailableISize(), &mBaseline); - nsIFrame* parent = GetParent(); - bool inNestedRuby = parent->StyleContext()->IsInlineDescendantOfRuby(); - // Allow line break between ruby bases when white-space allows, - // we are not inside a nested ruby, and there is no span. - bool allowLineBreak = !inNestedRuby && StyleText()->WhiteSpaceCanWrap(this); - bool allowInitialLineBreak = allowLineBreak; - if (!GetPrevInFlow()) { - allowInitialLineBreak = !inNestedRuby && - parent->StyleText()->WhiteSpaceCanWrap(parent); - } - if (!aReflowState.mLineLayout->LineIsBreakable()) { - allowInitialLineBreak = false; - } + bool allowInitialLineBreak, allowLineBreak; + GetIsLineBreakAllowed(this, aReflowState.mLineLayout->LineIsBreakable(), + &allowInitialLineBreak, &allowLineBreak); nscoord isize = 0; // Reflow columns excluding any span From 6fb97f335e79f276ab43fda1f1afad591f4f4df6 Mon Sep 17 00:00:00 2001 From: Xidorn Quan Date: Sun, 22 Feb 2015 00:39:33 +1300 Subject: [PATCH 23/48] Bug 1134432 part 5 - Fix line breaks for intrinsic min isize calculation of ruby base container. r=dbaron --- layout/generic/nsRubyBaseContainerFrame.cpp | 34 +++++++++++++++------ 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/layout/generic/nsRubyBaseContainerFrame.cpp b/layout/generic/nsRubyBaseContainerFrame.cpp index df188de476c..31bd6885118 100644 --- a/layout/generic/nsRubyBaseContainerFrame.cpp +++ b/layout/generic/nsRubyBaseContainerFrame.cpp @@ -201,7 +201,10 @@ RubyColumnEnumerator::GetColumn(RubyColumn& aColumn) const } static gfxBreakPriority -LineBreakBefore(const nsHTMLReflowState& aReflowState, nsRubyBaseFrame* aFrame) +LineBreakBefore(nsIFrame* aFrame, + nsRenderingContext* aRenderingContext, + nsIFrame* aLineContainerFrame, + const nsLineList::iterator* aLine) { for (nsIFrame* child = aFrame; child; child = child->GetFirstPrincipalChild()) { @@ -216,9 +219,8 @@ LineBreakBefore(const nsHTMLReflowState& aReflowState, nsRubyBaseFrame* aFrame) auto textFrame = static_cast(child); gfxSkipCharsIterator iter = textFrame->EnsureTextRun(nsTextFrame::eInflated, - aReflowState.rendContext->ThebesContext(), - aReflowState.mLineLayout->LineContainerFrame(), - aReflowState.mLineLayout->GetLine()); + aRenderingContext->ThebesContext(), + aLineContainerFrame, aLine); iter.SetOriginalOffset(textFrame->GetContentOffset()); uint32_t pos = iter.GetSkippedOffset(); gfxTextRun* textRun = textFrame->GetTextRun(nsTextFrame::eInflated); @@ -307,13 +309,25 @@ nsRubyBaseContainerFrame::AddInlineMinISize( } } + bool firstFrame = true; + bool allowInitialLineBreak, allowLineBreak; + GetIsLineBreakAllowed(this, !aData->atStartOfLine, + &allowInitialLineBreak, &allowLineBreak); for (nsIFrame* frame = this; frame; frame = frame->GetNextInFlow()) { RubyColumnEnumerator enumerator( static_cast(frame), textContainers); for (; !enumerator.AtEnd(); enumerator.Next()) { - // FIXME This simply assumes that there is an optional break - // between ruby columns, which is no true in many cases. - aData->OptionallyBreak(aRenderingContext); + if (firstFrame ? allowInitialLineBreak : allowLineBreak) { + nsIFrame* baseFrame = enumerator.GetFrameAtLevel(0); + if (baseFrame) { + gfxBreakPriority breakPriority = + LineBreakBefore(baseFrame, aRenderingContext, nullptr, nullptr); + if (breakPriority != gfxBreakPriority::eNoBreak) { + aData->OptionallyBreak(aRenderingContext); + } + } + } + firstFrame = false; nscoord isize = CalculateColumnPrefISize(aRenderingContext, enumerator); aData->currentLine += isize; if (isize > 0) { @@ -764,8 +778,10 @@ nsRubyBaseContainerFrame::ReflowOneColumn(const ReflowState& aReflowState, aReflowState.mAllowLineBreak : aReflowState.mAllowInitialLineBreak; if (allowBreakBefore) { bool shouldBreakBefore = false; - gfxBreakPriority breakPriority = - LineBreakBefore(baseReflowState, aColumn.mBaseFrame); + gfxBreakPriority breakPriority = LineBreakBefore( + aColumn.mBaseFrame, baseReflowState.rendContext, + baseReflowState.mLineLayout->LineContainerFrame(), + baseReflowState.mLineLayout->GetLine()); if (breakPriority != gfxBreakPriority::eNoBreak) { int32_t offset; gfxBreakPriority lastBreakPriority; From c3cf551c1d4c43338a2ea50170d42f538359f7ff Mon Sep 17 00:00:00 2001 From: Xidorn Quan Date: Sun, 22 Feb 2015 00:39:33 +1300 Subject: [PATCH 24/48] Bug 1134432 part 6 - Add reftest for intrinsic isize calculation of ruby. r=dbaron --- .../css-ruby/intrinsic-isize-1-ref.html | 49 +++++++++++++++++++ .../reftests/css-ruby/intrinsic-isize-1.html | 46 +++++++++++++++++ layout/reftests/css-ruby/reftest.list | 1 + 3 files changed, 96 insertions(+) create mode 100644 layout/reftests/css-ruby/intrinsic-isize-1-ref.html create mode 100644 layout/reftests/css-ruby/intrinsic-isize-1.html diff --git a/layout/reftests/css-ruby/intrinsic-isize-1-ref.html b/layout/reftests/css-ruby/intrinsic-isize-1-ref.html new file mode 100644 index 00000000000..902e8537c3c --- /dev/null +++ b/layout/reftests/css-ruby/intrinsic-isize-1-ref.html @@ -0,0 +1,49 @@ + + + + + Bug 1134432 - Intrinsic ISize calculation of ruby + + + +
+ ABCDEF +
+
+ ABCDEF +
+
+ +
+ XYZABCDEFXYZ +
+
+ XYZABCDEFXYZ +
+
+ +
+ あいうえ +
+
+ あいうえ +
+
+ +
+ おあいうえお +
+
+ おあいうえお +
+
+ + diff --git a/layout/reftests/css-ruby/intrinsic-isize-1.html b/layout/reftests/css-ruby/intrinsic-isize-1.html new file mode 100644 index 00000000000..dd900b4b707 --- /dev/null +++ b/layout/reftests/css-ruby/intrinsic-isize-1.html @@ -0,0 +1,46 @@ + + + + + Bug 1134432 - Intrinsic ISize calculation of ruby + + + +
+ ABCDEF +
+
+ ABCDEF +
+
+ +
+ XYZABCDEFXYZ +
+
+ XYZABCDEFXYZ +
+
+ +
+ あいうえ +
+
+ あいうえ +
+
+ +
+ おあいうえお +
+
+ おあいうえお +
+
+ + diff --git a/layout/reftests/css-ruby/reftest.list b/layout/reftests/css-ruby/reftest.list index cbfffd95d97..243ba0ac4f1 100644 --- a/layout/reftests/css-ruby/reftest.list +++ b/layout/reftests/css-ruby/reftest.list @@ -19,6 +19,7 @@ default-preferences pref(layout.css.ruby.enabled,true) == intra-level-whitespace-1.html intra-level-whitespace-1-ref.html == intra-level-whitespace-2.html intra-level-whitespace-2-ref.html == intra-level-whitespace-3.html intra-level-whitespace-3-ref.html +== intrinsic-isize-1.html intrinsic-isize-1-ref.html == justification-1.html justification-1-ref.html == justification-2.html justification-2-ref.html == line-breaking-1.html line-breaking-1-ref.html From b0542ec55aabb10ec2702518d12ecd1fef672ea1 Mon Sep 17 00:00:00 2001 From: Xidorn Quan Date: Sun, 22 Feb 2015 00:48:05 +1300 Subject: [PATCH 25/48] No bug - Fix some meta data of submitted w3c-css reftests. DONTBUILD --- layout/reftests/w3c-css/submitted/ruby/ruby-autohide-001.html | 2 +- layout/reftests/w3c-css/submitted/ruby/ruby-autohide-002.html | 2 +- layout/reftests/w3c-css/submitted/ruby/ruby-autohide-003.html | 2 +- .../w3c-css/submitted/ruby/ruby-inlinize-blocks-001.html | 2 +- .../w3c-css/submitted/ruby/ruby-inlinize-blocks-002.html | 2 +- .../w3c-css/submitted/ruby/ruby-inlinize-blocks-003.html | 2 +- .../w3c-css/submitted/ruby/ruby-inlinize-blocks-004.html | 2 +- .../w3c-css/submitted/ruby/ruby-inlinize-blocks-005.html | 2 +- .../w3c-css/submitted/text-decor-3/ruby-text-decoration-01.html | 1 + 9 files changed, 9 insertions(+), 8 deletions(-) diff --git a/layout/reftests/w3c-css/submitted/ruby/ruby-autohide-001.html b/layout/reftests/w3c-css/submitted/ruby/ruby-autohide-001.html index 6495e47ebc1..0b555751b2b 100644 --- a/layout/reftests/w3c-css/submitted/ruby/ruby-autohide-001.html +++ b/layout/reftests/w3c-css/submitted/ruby/ruby-autohide-001.html @@ -4,7 +4,7 @@ CSS Test: Autohide ruby annotations which are identical to their bases - + diff --git a/layout/reftests/w3c-css/submitted/ruby/ruby-autohide-002.html b/layout/reftests/w3c-css/submitted/ruby/ruby-autohide-002.html index d1137f208f5..77cc19852a7 100644 --- a/layout/reftests/w3c-css/submitted/ruby/ruby-autohide-002.html +++ b/layout/reftests/w3c-css/submitted/ruby/ruby-autohide-002.html @@ -4,7 +4,7 @@ CSS Test: Autohide ruby annotations which are identical to their bases - + diff --git a/dom/webidl/FormData.webidl b/dom/webidl/FormData.webidl index 4e4bef26850..1003cc06688 100644 --- a/dom/webidl/FormData.webidl +++ b/dom/webidl/FormData.webidl @@ -7,8 +7,17 @@ * http://xhr.spec.whatwg.org */ +typedef (File or USVString) FormDataEntryValue; + [Constructor(optional HTMLFormElement form)] interface FormData { - void append(DOMString name, Blob value, optional DOMString filename); - void append(DOMString name, DOMString value); + void append(USVString name, Blob value, optional USVString filename); + void append(USVString name, USVString value); + void delete(USVString name); + FormDataEntryValue? get(USVString name); + sequence getAll(USVString name); + boolean has(USVString name); + void set(USVString name, Blob value, optional USVString filename); + void set(USVString name, USVString value); + // iterable; - Bug 1127703 }; From 3979592cfd2b042b5b14073e435fb50e7e16d4ea Mon Sep 17 00:00:00 2001 From: Nikhil Marathe Date: Sat, 21 Feb 2015 11:54:44 -0800 Subject: [PATCH 47/48] Bug 1127150 - Fix FormData File filename. r=baku --- dom/base/nsFormData.cpp | 39 +++++++++++++++++++++++++------- dom/base/nsFormData.h | 2 -- dom/html/test/test_formData.html | 25 +++++++++++++++++--- 3 files changed, 53 insertions(+), 13 deletions(-) diff --git a/dom/base/nsFormData.cpp b/dom/base/nsFormData.cpp index da486d2cc2d..8116390c5d6 100644 --- a/dom/base/nsFormData.cpp +++ b/dom/base/nsFormData.cpp @@ -8,6 +8,8 @@ #include "mozilla/dom/File.h" #include "mozilla/dom/HTMLFormElement.h" +#include "MultipartFileImpl.h" + using namespace mozilla; using namespace mozilla::dom; @@ -75,13 +77,33 @@ void nsFormData::Append(const nsAString& aName, File& aBlob, const Optional& aFilename) { - nsString filename; - if (aFilename.WasPassed()) { - filename = aFilename.Value(); - } else { - filename.SetIsVoid(true); + // Step 3 "If value is a Blob object and not a File object, set value to + // a new File object, representing the same bytes, whose name attribute value + // is "blob"." + // Step 4 "If value is a File object and filename is given, set value to + // a new File object, representing the same bytes, whose name attribute + // value is filename." + nsString filename = NS_LITERAL_STRING("blob"); + if (aBlob.IsFile()) { + aBlob.GetName(filename); + if (aFilename.WasPassed()) { + filename = aFilename.Value(); + } } - AddNameFilePair(aName, &aBlob, filename); + + nsAutoTArray, 1> blobImpls; + blobImpls.AppendElement(aBlob.Impl()); + + nsAutoString contentType; + aBlob.GetType(contentType); + + nsRefPtr impl = + new MultipartFileImpl(blobImpls, filename, contentType); + + nsRefPtr file = new File(aBlob.GetParentObject(), impl); + nsAutoString voidString; + voidString.SetIsVoid(true); + AddNameFilePair(aName, file, voidString); } void @@ -264,10 +286,11 @@ nsFormData::GetSendInfo(nsIInputStream** aBody, uint64_t* aContentLength, { nsFSMultipartFormData fs(NS_LITERAL_CSTRING("UTF-8"), nullptr); + nsAutoString voidString; + voidString.SetIsVoid(true); for (uint32_t i = 0; i < mFormData.Length(); ++i) { if (mFormData[i].valueIsFile) { - fs.AddNameFilePair(mFormData[i].name, mFormData[i].fileValue, - mFormData[i].filename); + fs.AddNameFilePair(mFormData[i].name, mFormData[i].fileValue, voidString); } else { fs.AddNameValuePair(mFormData[i].name, mFormData[i].stringValue); diff --git a/dom/base/nsFormData.h b/dom/base/nsFormData.h index 97021cfc795..f171f557474 100644 --- a/dom/base/nsFormData.h +++ b/dom/base/nsFormData.h @@ -39,7 +39,6 @@ private: nsString name; nsString stringValue; nsRefPtr fileValue; - nsString filename; bool valueIsFile; }; @@ -66,7 +65,6 @@ private: MOZ_ASSERT(aData); aData->name = aName; aData->fileValue = aBlob; - aData->filename = aFilename; aData->valueIsFile = true; } diff --git a/dom/html/test/test_formData.html b/dom/html/test/test_formData.html index f39c940b0a5..5c8decd3855 100644 --- a/dom/html/test/test_formData.html +++ b/dom/html/test/test_formData.html @@ -97,6 +97,21 @@ function testIterate() { todo(false, "Implement this in Bug 1085284."); } +function testFilename() { + var f = new FormData(); + // Spec says if a Blob (which is not a File) is added, the name parameter is effectively ignored, always set to "blob". + f.append("blob", new Blob(["hi"]), "blob1.txt"); + is(f.get("blob").name, "blob", "Blob's filename should be blob irrespective of what was passed."); + + // Spec says if a File is added, the name parameter is respected if passed. + f.append("file1", new File(["hi"], "file1.txt")); + is(f.get("file1").name, "file1.txt", "File's filename should be original's name if no filename is explicitly passed."); + f.append("file2", new File(["hi"], "file2.txt"), "fakename.txt"); + is(f.get("file2").name, "fakename.txt", "File's filename should be explicitly passed name."); + f.append("file3", new File(["hi"], "")); + is(f.get("file3").name, "", "File's filename is returned even if empty."); +} + function testSend() { var xhr = new XMLHttpRequest(); xhr.open("POST", "form_submit_server.sjs"); @@ -112,10 +127,10 @@ function testSend() { 'form-data; name="empty"; filename="blob"'); is(response[1].headers['Content-Disposition'], - 'form-data; name="explicit"; filename="explicit-file-name"'); + 'form-data; name="explicit"; filename="blob"'); is(response[2].headers['Content-Disposition'], - 'form-data; name="explicit-empty"; filename=""'); + 'form-data; name="explicit-empty"; filename="blob"'); is(response[3].headers['Content-Disposition'], 'form-data; name="file-name"; filename="testname"'); @@ -133,10 +148,13 @@ function testSend() { var fd = new FormData(); fd.append("empty", blob); - fd.append("explicit", blob, "explicit-file-name"); + fd.append("explicit", blob, "explicit-file-name-will-be-ignored"); fd.append("explicit-empty", blob, ""); file = new File([blob], 'testname', {type: 'text/plain'}); fd.append("file-name", file); + // XXXnsm Empty filename will get converted to "blob" when sending to + // server, which I guess is acceptable since no spec seems to define + // anything. Blink does the same. file = new File([blob], '', {type: 'text/plain'}); fd.append("empty-file-name", file); file = new File([blob], 'testname', {type: 'text/plain'}); @@ -152,6 +170,7 @@ function runTest() { testDelete(); testSet(); testIterate(); + testFilename(); // Finally, send an XHR and verify the response matches. testSend(); } From 0bec52748a3da8e767a59fca26a7d691ddafee82 Mon Sep 17 00:00:00 2001 From: Ryan VanderMeulen Date: Sat, 21 Feb 2015 15:47:04 -0500 Subject: [PATCH 48/48] Backed out 2 changesets (bug 1127150, bug 1085283) for w-p-t failures. Backed out changeset cc77a5165615 (bug 1127150) Backed out changeset ef51eb31fa09 (bug 1085283) --- dom/base/nsFormData.cpp | 152 ++----------------------------- dom/base/nsFormData.h | 69 ++++---------- dom/html/nsFormSubmission.cpp | 28 +++--- dom/html/nsFormSubmission.h | 13 +-- dom/html/test/test_formData.html | 121 +----------------------- dom/webidl/FormData.webidl | 13 +-- 6 files changed, 53 insertions(+), 343 deletions(-) diff --git a/dom/base/nsFormData.cpp b/dom/base/nsFormData.cpp index 8116390c5d6..b1fd0d85551 100644 --- a/dom/base/nsFormData.cpp +++ b/dom/base/nsFormData.cpp @@ -6,9 +6,9 @@ #include "nsIVariant.h" #include "nsIInputStream.h" #include "mozilla/dom/File.h" +#include "mozilla/dom/File.h" #include "mozilla/dom/HTMLFormElement.h" - -#include "MultipartFileImpl.h" +#include "mozilla/dom/FormDataBinding.h" using namespace mozilla; using namespace mozilla::dom; @@ -77,146 +77,13 @@ void nsFormData::Append(const nsAString& aName, File& aBlob, const Optional& aFilename) { - // Step 3 "If value is a Blob object and not a File object, set value to - // a new File object, representing the same bytes, whose name attribute value - // is "blob"." - // Step 4 "If value is a File object and filename is given, set value to - // a new File object, representing the same bytes, whose name attribute - // value is filename." - nsString filename = NS_LITERAL_STRING("blob"); - if (aBlob.IsFile()) { - aBlob.GetName(filename); - if (aFilename.WasPassed()) { - filename = aFilename.Value(); - } - } - - nsAutoTArray, 1> blobImpls; - blobImpls.AppendElement(aBlob.Impl()); - - nsAutoString contentType; - aBlob.GetType(contentType); - - nsRefPtr impl = - new MultipartFileImpl(blobImpls, filename, contentType); - - nsRefPtr file = new File(aBlob.GetParentObject(), impl); - nsAutoString voidString; - voidString.SetIsVoid(true); - AddNameFilePair(aName, file, voidString); -} - -void -nsFormData::Delete(const nsAString& aName) -{ - // We have to use this slightly awkward for loop since uint32_t >= 0 is an - // error for being always true. - for (uint32_t i = mFormData.Length(); i-- > 0; ) { - if (aName.Equals(mFormData[i].name)) { - mFormData.RemoveElementAt(i); - } - } -} - -void -nsFormData::ExtractValue(const FormDataTuple& aTuple, - OwningFileOrUSVString* aOutValue) -{ - if (aTuple.valueIsFile) { - aOutValue->SetAsFile() = aTuple.fileValue; + nsString filename; + if (aFilename.WasPassed()) { + filename = aFilename.Value(); } else { - aOutValue->SetAsUSVString() = aTuple.stringValue; - } -} - -void -nsFormData::Get(const nsAString& aName, - Nullable& aOutValue) -{ - for (uint32_t i = 0; i < mFormData.Length(); ++i) { - if (aName.Equals(mFormData[i].name)) { - ExtractValue(mFormData[i], &aOutValue.SetValue()); - return; - } - } - - aOutValue.SetNull(); -} - -void -nsFormData::GetAll(const nsAString& aName, - nsTArray& aValues) -{ - for (uint32_t i = 0; i < mFormData.Length(); ++i) { - if (aName.Equals(mFormData[i].name)) { - OwningFileOrUSVString* element = aValues.AppendElement(); - ExtractValue(mFormData[i], element); - } - } -} - -bool -nsFormData::Has(const nsAString& aName) -{ - for (uint32_t i = 0; i < mFormData.Length(); ++i) { - if (aName.Equals(mFormData[i].name)) { - return true; - } - } - - return false; -} - -nsFormData::FormDataTuple* -nsFormData::RemoveAllOthersAndGetFirstFormDataTuple(const nsAString& aName) -{ - FormDataTuple* lastFoundTuple = nullptr; - uint32_t lastFoundIndex = mFormData.Length(); - // We have to use this slightly awkward for loop since uint32_t >= 0 is an - // error for being always true. - for (uint32_t i = mFormData.Length(); i-- > 0; ) { - if (aName.Equals(mFormData[i].name)) { - if (lastFoundTuple) { - // The one we found earlier was not the first one, we can remove it. - mFormData.RemoveElementAt(lastFoundIndex); - } - - lastFoundTuple = &mFormData[i]; - lastFoundIndex = i; - } - } - - return lastFoundTuple; -} - -void -nsFormData::Set(const nsAString& aName, File& aBlob, - const Optional& aFilename) -{ - FormDataTuple* tuple = RemoveAllOthersAndGetFirstFormDataTuple(aName); - if (tuple) { - nsAutoString filename; - if (aFilename.WasPassed()) { - filename = aFilename.Value(); - } else { - filename.SetIsVoid(true); - } - - SetNameFilePair(tuple, aName, &aBlob, filename); - } else { - Append(aName, aBlob, aFilename); - } -} - -void -nsFormData::Set(const nsAString& aName, const nsAString& aValue) -{ - FormDataTuple* tuple = RemoveAllOthersAndGetFirstFormDataTuple(aName); - if (tuple) { - SetNameValuePair(tuple, aName, aValue); - } else { - Append(aName, aValue); + filename.SetIsVoid(true); } + AddNameFilePair(aName, &aBlob, filename); } // ------------------------------------------------------------------------- @@ -286,11 +153,10 @@ nsFormData::GetSendInfo(nsIInputStream** aBody, uint64_t* aContentLength, { nsFSMultipartFormData fs(NS_LITERAL_CSTRING("UTF-8"), nullptr); - nsAutoString voidString; - voidString.SetIsVoid(true); for (uint32_t i = 0; i < mFormData.Length(); ++i) { if (mFormData[i].valueIsFile) { - fs.AddNameFilePair(mFormData[i].name, mFormData[i].fileValue, voidString); + fs.AddNameFilePair(mFormData[i].name, mFormData[i].fileValue, + mFormData[i].filename); } else { fs.AddNameValuePair(mFormData[i].name, mFormData[i].stringValue); diff --git a/dom/base/nsFormData.h b/dom/base/nsFormData.h index f171f557474..64e6c4ee719 100644 --- a/dom/base/nsFormData.h +++ b/dom/base/nsFormData.h @@ -13,7 +13,6 @@ #include "nsTArray.h" #include "mozilla/ErrorResult.h" #include "mozilla/dom/BindingDeclarations.h" -#include "mozilla/dom/FormDataBinding.h" namespace mozilla { class ErrorResult; @@ -30,46 +29,8 @@ class nsFormData MOZ_FINAL : public nsIDOMFormData, public nsFormSubmission, public nsWrapperCache { -private: ~nsFormData() {} - typedef mozilla::dom::File File; - struct FormDataTuple - { - nsString name; - nsString stringValue; - nsRefPtr fileValue; - bool valueIsFile; - }; - - // Returns the FormDataTuple to modify. This may be null, in which case - // no element with aName was found. - FormDataTuple* - RemoveAllOthersAndGetFirstFormDataTuple(const nsAString& aName); - - void SetNameValuePair(FormDataTuple* aData, - const nsAString& aName, - const nsAString& aValue) - { - MOZ_ASSERT(aData); - aData->name = aName; - aData->stringValue = aValue; - aData->valueIsFile = false; - } - - void SetNameFilePair(FormDataTuple* aData, - const nsAString& aName, - File* aBlob, - const nsAString& aFilename) - { - MOZ_ASSERT(aData); - aData->name = aName; - aData->fileValue = aBlob; - aData->valueIsFile = true; - } - - void ExtractValue(const FormDataTuple& aTuple, - mozilla::dom::OwningFileOrUSVString* aOutValue); public: explicit nsFormData(nsISupports* aOwner = nullptr); @@ -94,15 +55,8 @@ public: const mozilla::dom::Optional >& aFormElement, mozilla::ErrorResult& aRv); void Append(const nsAString& aName, const nsAString& aValue); - void Append(const nsAString& aName, File& aBlob, + void Append(const nsAString& aName, mozilla::dom::File& aBlob, const mozilla::dom::Optional& aFilename); - void Delete(const nsAString& aName); - void Get(const nsAString& aName, mozilla::dom::Nullable& aOutValue); - void GetAll(const nsAString& aName, nsTArray& aValues); - bool Has(const nsAString& aName); - void Set(const nsAString& aName, File& aBlob, - const mozilla::dom::Optional& aFilename); - void Set(const nsAString& aName, const nsAString& aValue); // nsFormSubmission virtual nsresult GetEncodedSubmission(nsIURI* aURI, @@ -111,20 +65,35 @@ public: const nsAString& aValue) MOZ_OVERRIDE { FormDataTuple* data = mFormData.AppendElement(); - SetNameValuePair(data, aName, aValue); + data->name = aName; + data->stringValue = aValue; + data->valueIsFile = false; return NS_OK; } virtual nsresult AddNameFilePair(const nsAString& aName, - File* aBlob, + nsIDOMBlob* aBlob, const nsString& aFilename) MOZ_OVERRIDE { FormDataTuple* data = mFormData.AppendElement(); - SetNameFilePair(data, aName, aBlob, aFilename); + data->name = aName; + data->fileValue = aBlob; + data->filename = aFilename; + data->valueIsFile = true; return NS_OK; } + private: nsCOMPtr mOwner; + struct FormDataTuple + { + nsString name; + nsString stringValue; + nsCOMPtr fileValue; + nsString filename; + bool valueIsFile; + }; + nsTArray mFormData; }; diff --git a/dom/html/nsFormSubmission.cpp b/dom/html/nsFormSubmission.cpp index 984b7d8d395..24336b7fcf4 100644 --- a/dom/html/nsFormSubmission.cpp +++ b/dom/html/nsFormSubmission.cpp @@ -38,7 +38,6 @@ #include "nsContentUtils.h" #include "mozilla/dom/EncodingUtils.h" -#include "mozilla/dom/File.h" using namespace mozilla; using mozilla::dom::EncodingUtils; @@ -79,7 +78,7 @@ public: virtual nsresult AddNameValuePair(const nsAString& aName, const nsAString& aValue); virtual nsresult AddNameFilePair(const nsAString& aName, - File* aBlob, + nsIDOMBlob* aBlob, const nsString& aFilename); virtual nsresult GetEncodedSubmission(nsIURI* aURI, nsIInputStream** aPostDataStream); @@ -166,7 +165,7 @@ nsFSURLEncoded::AddIsindex(const nsAString& aValue) nsresult nsFSURLEncoded::AddNameFilePair(const nsAString& aName, - File* aBlob, + nsIDOMBlob* aBlob, const nsString& aFilename) { if (!mWarnedFileControl) { @@ -175,8 +174,9 @@ nsFSURLEncoded::AddNameFilePair(const nsAString& aName, } nsAutoString filename; - if (aBlob && aBlob->IsFile()) { - aBlob->GetName(filename); + nsCOMPtr file = do_QueryInterface(aBlob); + if (file) { + file->GetName(filename); } return AddNameValuePair(aName, filename); @@ -441,7 +441,7 @@ nsFSMultipartFormData::AddNameValuePair(const nsAString& aName, nsresult nsFSMultipartFormData::AddNameFilePair(const nsAString& aName, - File* aBlob, + nsIDOMBlob* aBlob, const nsString& aFilename) { // Encode the control name @@ -459,8 +459,9 @@ nsFSMultipartFormData::AddNameFilePair(const nsAString& aName, } else { // Get and encode the filename nsAutoString filename16; - if (aBlob->IsFile()) { - rv = aBlob->GetName(filename16); + nsCOMPtr file = do_QueryInterface(aBlob); + if (file) { + rv = file->GetName(filename16); NS_ENSURE_SUCCESS(rv, rv); } @@ -468,7 +469,7 @@ nsFSMultipartFormData::AddNameFilePair(const nsAString& aName, filename16.AssignLiteral("blob"); } else { nsAutoString filepath16; - rv = aBlob->GetPath(filepath16); + rv = file->GetPath(filepath16); NS_ENSURE_SUCCESS(rv, rv); if (!filepath16.IsEmpty()) { // File.path includes trailing "/" @@ -597,7 +598,7 @@ public: virtual nsresult AddNameValuePair(const nsAString& aName, const nsAString& aValue); virtual nsresult AddNameFilePair(const nsAString& aName, - File* aBlob, + nsIDOMBlob* aBlob, const nsString& aFilename); virtual nsresult GetEncodedSubmission(nsIURI* aURI, nsIInputStream** aPostDataStream); @@ -621,12 +622,13 @@ nsFSTextPlain::AddNameValuePair(const nsAString& aName, nsresult nsFSTextPlain::AddNameFilePair(const nsAString& aName, - File* aBlob, + nsIDOMBlob* aBlob, const nsString& aFilename) { nsAutoString filename; - if (aBlob && aBlob->IsFile()) { - aBlob->GetName(filename); + nsCOMPtr file = do_QueryInterface(aBlob); + if (file) { + file->GetName(filename); } AddNameValuePair(aName, filename); diff --git a/dom/html/nsFormSubmission.h b/dom/html/nsFormSubmission.h index d7b5e0d329c..27802f0fb94 100644 --- a/dom/html/nsFormSubmission.h +++ b/dom/html/nsFormSubmission.h @@ -20,12 +20,7 @@ class nsIDocShell; class nsIRequest; class nsISaveAsCharset; class nsIMultiplexInputStream; - -namespace mozilla { -namespace dom { -class File; -} // namespace dom -} // namespace mozilla +class nsIDOMBlob; /** * Class for form submissions; encompasses the function to call to submit as @@ -56,9 +51,9 @@ public: * @param aFilename the filename to be used (not void) */ virtual nsresult AddNameFilePair(const nsAString& aName, - mozilla::dom::File* aBlob, + nsIDOMBlob* aBlob, const nsString& aFilename) = 0; - + /** * Reports whether the instance supports AddIsindex(). * @@ -166,7 +161,7 @@ public: virtual nsresult AddNameValuePair(const nsAString& aName, const nsAString& aValue) MOZ_OVERRIDE; virtual nsresult AddNameFilePair(const nsAString& aName, - mozilla::dom::File* aBlob, + nsIDOMBlob* aBlob, const nsString& aFilename) MOZ_OVERRIDE; virtual nsresult GetEncodedSubmission(nsIURI* aURI, nsIInputStream** aPostDataStream) MOZ_OVERRIDE; diff --git a/dom/html/test/test_formData.html b/dom/html/test/test_formData.html index 5c8decd3855..9248debd9e4 100644 --- a/dom/html/test/test_formData.html +++ b/dom/html/test/test_formData.html @@ -14,105 +14,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=690659 diff --git a/dom/webidl/FormData.webidl b/dom/webidl/FormData.webidl index 1003cc06688..4e4bef26850 100644 --- a/dom/webidl/FormData.webidl +++ b/dom/webidl/FormData.webidl @@ -7,17 +7,8 @@ * http://xhr.spec.whatwg.org */ -typedef (File or USVString) FormDataEntryValue; - [Constructor(optional HTMLFormElement form)] interface FormData { - void append(USVString name, Blob value, optional USVString filename); - void append(USVString name, USVString value); - void delete(USVString name); - FormDataEntryValue? get(USVString name); - sequence getAll(USVString name); - boolean has(USVString name); - void set(USVString name, Blob value, optional USVString filename); - void set(USVString name, USVString value); - // iterable; - Bug 1127703 + void append(DOMString name, Blob value, optional DOMString filename); + void append(DOMString name, DOMString value); };