Merge f-t to m-c, a=merge

This commit is contained in:
Phil Ringnalda 2016-09-30 23:25:36 -07:00
commit f3a11c5cdb
46 changed files with 11095 additions and 4470 deletions

View File

@ -1,3 +1,3 @@
This is the pdf.js project output, https://github.com/mozilla/pdf.js This is the pdf.js project output, https://github.com/mozilla/pdf.js
Current extension version is: 1.5.476 Current extension version is: 1.5.498

View File

@ -28,8 +28,8 @@ factory((root.pdfjsDistBuildPdf = {}));
// Use strict in our context only - users might not want it // Use strict in our context only - users might not want it
'use strict'; 'use strict';
var pdfjsVersion = '1.5.476'; var pdfjsVersion = '1.5.498';
var pdfjsBuild = 'c0e82db'; var pdfjsBuild = '1564dc3';
var pdfjsFilePath = var pdfjsFilePath =
typeof document !== 'undefined' && document.currentScript ? typeof document !== 'undefined' && document.currentScript ?
@ -115,25 +115,25 @@ var AnnotationFlag = {
}; };
var AnnotationFieldFlag = { var AnnotationFieldFlag = {
READONLY: 1, READONLY: 0x0000001,
REQUIRED: 2, REQUIRED: 0x0000002,
NOEXPORT: 3, NOEXPORT: 0x0000004,
MULTILINE: 13, MULTILINE: 0x0001000,
PASSWORD: 14, PASSWORD: 0x0002000,
NOTOGGLETOOFF: 15, NOTOGGLETOOFF: 0x0004000,
RADIO: 16, RADIO: 0x0008000,
PUSHBUTTON: 17, PUSHBUTTON: 0x0010000,
COMBO: 18, COMBO: 0x0020000,
EDIT: 19, EDIT: 0x0040000,
SORT: 20, SORT: 0x0080000,
FILESELECT: 21, FILESELECT: 0x0100000,
MULTISELECT: 22, MULTISELECT: 0x0200000,
DONOTSPELLCHECK: 23, DONOTSPELLCHECK: 0x0400000,
DONOTSCROLL: 24, DONOTSCROLL: 0x0800000,
COMB: 25, COMB: 0x1000000,
RICHTEXT: 26, RICHTEXT: 0x2000000,
RADIOSINUNISON: 26, RADIOSINUNISON: 0x2000000,
COMMITONSELCHANGE: 27, COMMITONSELCHANGE: 0x4000000,
}; };
var AnnotationBorderStyleType = { var AnnotationBorderStyleType = {
@ -2365,19 +2365,31 @@ var TextWidgetAnnotationElement = (
var element = null; var element = null;
if (this.renderInteractiveForms) { if (this.renderInteractiveForms) {
// NOTE: We cannot set the values using `element.value` below, since it
// prevents the AnnotationLayer rasterizer in `test/driver.js`
// from parsing the elements correctly for the reference tests.
if (this.data.multiLine) { if (this.data.multiLine) {
element = document.createElement('textarea'); element = document.createElement('textarea');
element.textContent = this.data.fieldValue;
} else { } else {
element = document.createElement('input'); element = document.createElement('input');
element.type = 'text'; element.type = 'text';
element.setAttribute('value', this.data.fieldValue);
} }
element.value = this.data.fieldValue;
element.disabled = this.data.readOnly; element.disabled = this.data.readOnly;
if (this.data.maxLen !== null) { if (this.data.maxLen !== null) {
element.maxLength = this.data.maxLen; element.maxLength = this.data.maxLen;
} }
if (this.data.comb) {
var fieldWidth = this.data.rect[2] - this.data.rect[0];
var combWidth = fieldWidth / this.data.maxLen;
element.classList.add('comb');
element.style.letterSpacing = 'calc(' + combWidth + 'px - 1ch)';
}
} else { } else {
element = document.createElement('div'); element = document.createElement('div');
element.textContent = this.data.fieldValue; element.textContent = this.data.fieldValue;

View File

@ -28,8 +28,8 @@ factory((root.pdfjsDistBuildPdfWorker = {}));
// Use strict in our context only - users might not want it // Use strict in our context only - users might not want it
'use strict'; 'use strict';
var pdfjsVersion = '1.5.476'; var pdfjsVersion = '1.5.498';
var pdfjsBuild = 'c0e82db'; var pdfjsBuild = '1564dc3';
var pdfjsFilePath = var pdfjsFilePath =
typeof document !== 'undefined' && document.currentScript ? typeof document !== 'undefined' && document.currentScript ?
@ -1101,25 +1101,25 @@ var AnnotationFlag = {
}; };
var AnnotationFieldFlag = { var AnnotationFieldFlag = {
READONLY: 1, READONLY: 0x0000001,
REQUIRED: 2, REQUIRED: 0x0000002,
NOEXPORT: 3, NOEXPORT: 0x0000004,
MULTILINE: 13, MULTILINE: 0x0001000,
PASSWORD: 14, PASSWORD: 0x0002000,
NOTOGGLETOOFF: 15, NOTOGGLETOOFF: 0x0004000,
RADIO: 16, RADIO: 0x0008000,
PUSHBUTTON: 17, PUSHBUTTON: 0x0010000,
COMBO: 18, COMBO: 0x0020000,
EDIT: 19, EDIT: 0x0040000,
SORT: 20, SORT: 0x0080000,
FILESELECT: 21, FILESELECT: 0x0100000,
MULTISELECT: 22, MULTISELECT: 0x0200000,
DONOTSPELLCHECK: 23, DONOTSPELLCHECK: 0x0400000,
DONOTSCROLL: 24, DONOTSCROLL: 0x0800000,
COMB: 25, COMB: 0x1000000,
RICHTEXT: 26, RICHTEXT: 0x2000000,
RADIOSINUNISON: 26, RADIOSINUNISON: 0x2000000,
COMMITONSELCHANGE: 27, COMMITONSELCHANGE: 0x4000000,
}; };
var AnnotationBorderStyleType = { var AnnotationBorderStyleType = {
@ -10214,7 +10214,7 @@ var error = sharedUtil.error;
* (partners.adobe.com/public/developer/en/ps/sdk/5116.DCT_Filter.pdf) * (partners.adobe.com/public/developer/en/ps/sdk/5116.DCT_Filter.pdf)
*/ */
var JpegImage = (function jpegImage() { var JpegImage = (function JpegImageClosure() {
var dctZigZag = new Uint8Array([ var dctZigZag = new Uint8Array([
0, 0,
1, 8, 1, 8,
@ -10242,7 +10242,9 @@ var JpegImage = (function jpegImage() {
var dctSqrt2 = 5793; // sqrt(2) var dctSqrt2 = 5793; // sqrt(2)
var dctSqrt1d2 = 2896; // sqrt(2) / 2 var dctSqrt1d2 = 2896; // sqrt(2) / 2
function constructor() { function JpegImage() {
this.decodeTransform = null;
this.colorTransform = -1;
} }
function buildHuffmanTable(codeLengths, values) { function buildHuffmanTable(codeLengths, values) {
@ -10537,6 +10539,12 @@ var JpegImage = (function jpegImage() {
// find marker // find marker
bitsCount = 0; bitsCount = 0;
marker = (data[offset] << 8) | data[offset + 1]; marker = (data[offset] << 8) | data[offset + 1];
// Some bad images seem to pad Scan blocks with zero bytes, skip past
// those to attempt to find a valid marker (fixes issue4090.pdf).
while (data[offset] === 0x00 && offset < data.length - 1) {
offset++;
marker = (data[offset] << 8) | data[offset + 1];
}
if (marker <= 0xFF00) { if (marker <= 0xFF00) {
error('JPEG error: marker was not found'); error('JPEG error: marker was not found');
} }
@ -10759,7 +10767,7 @@ var JpegImage = (function jpegImage() {
return a <= 0 ? 0 : a >= 255 ? 255 : a; return a <= 0 ? 0 : a >= 255 ? 255 : a;
} }
constructor.prototype = { JpegImage.prototype = {
parse: function parse(data) { parse: function parse(data) {
function readUint16() { function readUint16() {
@ -11076,8 +11084,20 @@ var JpegImage = (function jpegImage() {
// The adobe transform marker overrides any previous setting // The adobe transform marker overrides any previous setting
return true; return true;
} else if (this.numComponents === 3) { } else if (this.numComponents === 3) {
if (!this.adobe && this.colorTransform === 0) {
// If the Adobe transform marker is not present and the image
// dictionary has a 'ColorTransform' entry, explicitly set to `0`,
// then the colours should *not* be transformed.
return false;
}
return true; return true;
} else { } else { // `this.numComponents !== 3`
if (!this.adobe && this.colorTransform === 1) {
// If the Adobe transform marker is not present and the image
// dictionary has a 'ColorTransform' entry, explicitly set to `1`,
// then the colours should be transformed.
return true;
}
return false; return false;
} }
}, },
@ -11219,7 +11239,7 @@ var JpegImage = (function jpegImage() {
rgbData[offset++] = grayColor; rgbData[offset++] = grayColor;
} }
return rgbData; return rgbData;
} else if (this.numComponents === 3) { } else if (this.numComponents === 3 && this._isColorConversionNeeded()) {
return this._convertYccToRgb(data); return this._convertYccToRgb(data);
} else if (this.numComponents === 4) { } else if (this.numComponents === 4) {
if (this._isColorConversionNeeded()) { if (this._isColorConversionNeeded()) {
@ -11236,7 +11256,7 @@ var JpegImage = (function jpegImage() {
} }
}; };
return constructor; return JpegImage;
})(); })();
exports.JpegImage = JpegImage; exports.JpegImage = JpegImage;
@ -18829,6 +18849,7 @@ exports.isStream = isStream;
var Util = sharedUtil.Util; var Util = sharedUtil.Util;
var error = sharedUtil.error; var error = sharedUtil.error;
var info = sharedUtil.info; var info = sharedUtil.info;
var isInt = sharedUtil.isInt;
var isArray = sharedUtil.isArray; var isArray = sharedUtil.isArray;
var createObjectURL = sharedUtil.createObjectURL; var createObjectURL = sharedUtil.createObjectURL;
var shadow = sharedUtil.shadow; var shadow = sharedUtil.shadow;
@ -19686,7 +19707,7 @@ var PredictorStream = (function PredictorStreamClosure() {
* DecodeStreams. * DecodeStreams.
*/ */
var JpegStream = (function JpegStreamClosure() { var JpegStream = (function JpegStreamClosure() {
function JpegStream(stream, maybeLength, dict, xref) { function JpegStream(stream, maybeLength, dict) {
// Some images may contain 'junk' before the SOI (start-of-image) marker. // Some images may contain 'junk' before the SOI (start-of-image) marker.
// Note: this seems to mainly affect inline images. // Note: this seems to mainly affect inline images.
var ch; var ch;
@ -19720,8 +19741,8 @@ var JpegStream = (function JpegStreamClosure() {
var jpegImage = new JpegImage(); var jpegImage = new JpegImage();
// Checking if values need to be transformed before conversion. // Checking if values need to be transformed before conversion.
if (this.forceRGB && this.dict && isArray(this.dict.get('Decode'))) { var decodeArr = this.dict.getArray('Decode', 'D');
var decodeArr = this.dict.getArray('Decode'); if (this.forceRGB && isArray(decodeArr)) {
var bitsPerComponent = this.dict.get('BitsPerComponent') || 8; var bitsPerComponent = this.dict.get('BitsPerComponent') || 8;
var decodeArrLength = decodeArr.length; var decodeArrLength = decodeArr.length;
var transform = new Int32Array(decodeArrLength); var transform = new Int32Array(decodeArrLength);
@ -19738,6 +19759,14 @@ var JpegStream = (function JpegStreamClosure() {
jpegImage.decodeTransform = transform; jpegImage.decodeTransform = transform;
} }
} }
// Fetching the 'ColorTransform' entry, if it exists.
var decodeParams = this.dict.get('DecodeParms', 'DP');
if (isDict(decodeParams)) {
var colorTransform = decodeParams.get('ColorTransform');
if (isInt(colorTransform)) {
jpegImage.colorTransform = colorTransform;
}
}
jpegImage.parse(this.bytes); jpegImage.parse(this.bytes);
var data = jpegImage.getData(this.drawWidth, this.drawHeight, var data = jpegImage.getData(this.drawWidth, this.drawHeight,
@ -19859,7 +19888,7 @@ var Jbig2Stream = (function Jbig2StreamClosure() {
var jbig2Image = new Jbig2Image(); var jbig2Image = new Jbig2Image();
var chunks = []; var chunks = [];
var decodeParams = this.dict.getArray('DecodeParms'); var decodeParams = this.dict.getArray('DecodeParms', 'DP');
// According to the PDF specification, DecodeParms can be either // According to the PDF specification, DecodeParms can be either
// a dictionary, or an array whose elements are dictionaries. // a dictionary, or an array whose elements are dictionaries.
@ -24663,7 +24692,7 @@ var Parser = (function ParserClosure() {
} }
if (name === 'DCTDecode' || name === 'DCT') { if (name === 'DCTDecode' || name === 'DCT') {
xrefStreamStats[StreamType.DCT] = true; xrefStreamStats[StreamType.DCT] = true;
return new JpegStream(stream, maybeLength, stream.dict, this.xref); return new JpegStream(stream, maybeLength, stream.dict);
} }
if (name === 'JPXDecode' || name === 'JPX') { if (name === 'JPXDecode' || name === 'JPX') {
xrefStreamStats[StreamType.JPX] = true; xrefStreamStats[StreamType.JPX] = true;
@ -30104,7 +30133,14 @@ var Type1Font = (function Type1FontClosure() {
var charStringsIndex = new CFFIndex(); var charStringsIndex = new CFFIndex();
charStringsIndex.add([0x8B, 0x0E]); // .notdef charStringsIndex.add([0x8B, 0x0E]); // .notdef
for (i = 0; i < count; i++) { for (i = 0; i < count; i++) {
charStringsIndex.add(glyphs[i]); var glyph = glyphs[i];
// If the CharString outline is empty, replace it with .notdef to
// prevent OTS from rejecting the font (fixes bug1252420.pdf).
if (glyph.length === 0) {
charStringsIndex.add([0x8B, 0x0E]); // .notdef
continue;
}
charStringsIndex.add(glyph);
} }
cff.charStrings = charStringsIndex; cff.charStrings = charStringsIndex;
@ -36074,18 +36110,26 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
*/ */
NativeImageDecoder.isSupported = NativeImageDecoder.isSupported =
function NativeImageDecoder_isSupported(image, xref, res) { function NativeImageDecoder_isSupported(image, xref, res) {
var cs = ColorSpace.parse(image.dict.get('ColorSpace', 'CS'), xref, res); var dict = image.dict;
if (dict.has('DecodeParms') || dict.has('DP')) {
return false;
}
var cs = ColorSpace.parse(dict.get('ColorSpace', 'CS'), xref, res);
return (cs.name === 'DeviceGray' || cs.name === 'DeviceRGB') && return (cs.name === 'DeviceGray' || cs.name === 'DeviceRGB') &&
cs.isDefaultDecode(image.dict.getArray('Decode', 'D')); cs.isDefaultDecode(dict.getArray('Decode', 'D'));
}; };
/** /**
* Checks if the image can be decoded by the browser. * Checks if the image can be decoded by the browser.
*/ */
NativeImageDecoder.isDecodable = NativeImageDecoder.isDecodable =
function NativeImageDecoder_isDecodable(image, xref, res) { function NativeImageDecoder_isDecodable(image, xref, res) {
var cs = ColorSpace.parse(image.dict.get('ColorSpace', 'CS'), xref, res); var dict = image.dict;
if (dict.has('DecodeParms') || dict.has('DP')) {
return false;
}
var cs = ColorSpace.parse(dict.get('ColorSpace', 'CS'), xref, res);
return (cs.numComps === 1 || cs.numComps === 3) && return (cs.numComps === 1 || cs.numComps === 3) &&
cs.isDefaultDecode(image.dict.getArray('Decode', 'D')); cs.isDefaultDecode(dict.getArray('Decode', 'D'));
}; };
function PartialEvaluator(pdfManager, xref, handler, pageIndex, function PartialEvaluator(pdfManager, xref, handler, pageIndex,
@ -39286,12 +39330,10 @@ AnnotationFactory.prototype = /** @lends AnnotationFactory.prototype */ {
* @param {Object} ref * @param {Object} ref
* @param {string} uniquePrefix * @param {string} uniquePrefix
* @param {Object} idCounters * @param {Object} idCounters
* @param {boolean} renderInteractiveForms
* @returns {Annotation} * @returns {Annotation}
*/ */
create: function AnnotationFactory_create(xref, ref, create: function AnnotationFactory_create(xref, ref,
uniquePrefix, idCounters, uniquePrefix, idCounters) {
renderInteractiveForms) {
var dict = xref.fetchIfRef(ref); var dict = xref.fetchIfRef(ref);
if (!isDict(dict)) { if (!isDict(dict)) {
return; return;
@ -39310,7 +39352,6 @@ AnnotationFactory.prototype = /** @lends AnnotationFactory.prototype */ {
ref: isRef(ref) ? ref : null, ref: isRef(ref) ? ref : null,
subtype: subtype, subtype: subtype,
id: id, id: id,
renderInteractiveForms: renderInteractiveForms,
}; };
switch (subtype) { switch (subtype) {
@ -39635,7 +39676,8 @@ var Annotation = (function AnnotationClosure() {
}.bind(this)); }.bind(this));
}, },
getOperatorList: function Annotation_getOperatorList(evaluator, task) { getOperatorList: function Annotation_getOperatorList(evaluator, task,
renderForms) {
if (!this.appearance) { if (!this.appearance) {
return Promise.resolve(new OperatorList()); return Promise.resolve(new OperatorList());
} }
@ -39672,13 +39714,13 @@ var Annotation = (function AnnotationClosure() {
}; };
Annotation.appendToOperatorList = function Annotation_appendToOperatorList( Annotation.appendToOperatorList = function Annotation_appendToOperatorList(
annotations, opList, partialEvaluator, task, intent) { annotations, opList, partialEvaluator, task, intent, renderForms) {
var annotationPromises = []; var annotationPromises = [];
for (var i = 0, n = annotations.length; i < n; ++i) { for (var i = 0, n = annotations.length; i < n; ++i) {
if ((intent === 'display' && annotations[i].viewable) || if ((intent === 'display' && annotations[i].viewable) ||
(intent === 'print' && annotations[i].printable)) { (intent === 'print' && annotations[i].printable)) {
annotationPromises.push( annotationPromises.push(
annotations[i].getOperatorList(partialEvaluator, task)); annotations[i].getOperatorList(partialEvaluator, task, renderForms));
} }
} }
return Promise.all(annotationPromises).then(function(operatorLists) { return Promise.all(annotationPromises).then(function(operatorLists) {
@ -39896,14 +39938,13 @@ var WidgetAnnotation = (function WidgetAnnotationClosure() {
* *
* @public * @public
* @memberof WidgetAnnotation * @memberof WidgetAnnotation
* @param {number} flag - Bit position, numbered from one instead of * @param {number} flag - Hexadecimal representation for an annotation
* zero, to check * field characteristic
* @return {boolean} * @return {boolean}
* @see {@link shared/util.js} * @see {@link shared/util.js}
*/ */
hasFieldFlag: function WidgetAnnotation_hasFieldFlag(flag) { hasFieldFlag: function WidgetAnnotation_hasFieldFlag(flag) {
var mask = 1 << (flag - 1); return !!(this.data.fieldFlags & flag);
return !!(this.data.fieldFlags & mask);
}, },
}); });
@ -39914,8 +39955,6 @@ var TextWidgetAnnotation = (function TextWidgetAnnotationClosure() {
function TextWidgetAnnotation(params) { function TextWidgetAnnotation(params) {
WidgetAnnotation.call(this, params); WidgetAnnotation.call(this, params);
this.renderInteractiveForms = params.renderInteractiveForms;
// Determine the alignment of text in the field. // Determine the alignment of text in the field.
var alignment = Util.getInheritableProperty(params.dict, 'Q'); var alignment = Util.getInheritableProperty(params.dict, 'Q');
if (!isInt(alignment) || alignment < 0 || alignment > 2) { if (!isInt(alignment) || alignment < 0 || alignment > 2) {
@ -39933,21 +39972,28 @@ var TextWidgetAnnotation = (function TextWidgetAnnotationClosure() {
// Process field flags for the display layer. // Process field flags for the display layer.
this.data.readOnly = this.hasFieldFlag(AnnotationFieldFlag.READONLY); this.data.readOnly = this.hasFieldFlag(AnnotationFieldFlag.READONLY);
this.data.multiLine = this.hasFieldFlag(AnnotationFieldFlag.MULTILINE); this.data.multiLine = this.hasFieldFlag(AnnotationFieldFlag.MULTILINE);
this.data.comb = this.hasFieldFlag(AnnotationFieldFlag.COMB) &&
!this.hasFieldFlag(AnnotationFieldFlag.MULTILINE) &&
!this.hasFieldFlag(AnnotationFieldFlag.PASSWORD) &&
!this.hasFieldFlag(AnnotationFieldFlag.FILESELECT) &&
this.data.maxLen !== null;
} }
Util.inherit(TextWidgetAnnotation, WidgetAnnotation, { Util.inherit(TextWidgetAnnotation, WidgetAnnotation, {
getOperatorList: function TextWidgetAnnotation_getOperatorList(evaluator, getOperatorList:
task) { function TextWidgetAnnotation_getOperatorList(evaluator, task,
renderForms) {
var operatorList = new OperatorList(); var operatorList = new OperatorList();
// Do not render form elements on the canvas when interactive forms are // Do not render form elements on the canvas when interactive forms are
// enabled. The display layer is responsible for rendering them instead. // enabled. The display layer is responsible for rendering them instead.
if (this.renderInteractiveForms) { if (renderForms) {
return Promise.resolve(operatorList); return Promise.resolve(operatorList);
} }
if (this.appearance) { if (this.appearance) {
return Annotation.prototype.getOperatorList.call(this, evaluator, task); return Annotation.prototype.getOperatorList.call(this, evaluator, task,
renderForms);
} }
// Even if there is an appearance stream, ignore it. This is the // Even if there is an appearance stream, ignore it. This is the
@ -40448,8 +40494,6 @@ var Page = (function PageClosure() {
}); });
}); });
this.renderInteractiveForms = renderInteractiveForms;
var annotationsPromise = pdfManager.ensure(this, 'annotations'); var annotationsPromise = pdfManager.ensure(this, 'annotations');
return Promise.all([pageListPromise, annotationsPromise]).then( return Promise.all([pageListPromise, annotationsPromise]).then(
function(datas) { function(datas) {
@ -40462,7 +40506,8 @@ var Page = (function PageClosure() {
} }
var annotationsReadyPromise = Annotation.appendToOperatorList( var annotationsReadyPromise = Annotation.appendToOperatorList(
annotations, pageOpList, partialEvaluator, task, intent); annotations, pageOpList, partialEvaluator, task, intent,
renderInteractiveForms);
return annotationsReadyPromise.then(function () { return annotationsReadyPromise.then(function () {
pageOpList.flush(true); pageOpList.flush(true);
return pageOpList; return pageOpList;
@ -40533,8 +40578,7 @@ var Page = (function PageClosure() {
var annotationRef = annotationRefs[i]; var annotationRef = annotationRefs[i];
var annotation = annotationFactory.create(this.xref, annotationRef, var annotation = annotationFactory.create(this.xref, annotationRef,
this.uniquePrefix, this.uniquePrefix,
this.idCounters, this.idCounters);
this.renderInteractiveForms);
if (annotation) { if (annotation) {
annotations.push(annotation); annotations.push(annotation);
} }

View File

@ -137,6 +137,22 @@
border: 1px solid transparent; border: 1px solid transparent;
} }
.annotationLayer .textWidgetAnnotation input.comb {
font-family: monospace;
padding-left: 2px;
padding-right: 0;
}
.annotationLayer .textWidgetAnnotation input.comb:focus {
/*
* Letter spacing is placed on the right side of each character. Hence, the
* letter spacing of the last character may be placed outside the visible
* area, causing horizontal scrolling. We avoid this by extending the width
* when the element has focus and revert this when it loses focus.
*/
width: 115%;
}
.annotationLayer .popupWrapper { .annotationLayer .popupWrapper {
position: absolute; position: absolute;
width: 20em; width: 20em;

File diff suppressed because it is too large Load Diff

View File

@ -10,6 +10,9 @@
// There are animations in the test page and since, by default, the <body> node // There are animations in the test page and since, by default, the <body> node
// is selected, animations will be displayed in the timeline, so the timeline // is selected, animations will be displayed in the timeline, so the timeline
// play/resume button will be displayed // play/resume button will be displayed
requestLongerTimeout(2);
add_task(function* () { add_task(function* () {
requestLongerTimeout(2); requestLongerTimeout(2);

File diff suppressed because it is too large Load Diff

View File

@ -14,7 +14,6 @@ const svg = {
"globe": require("./globe.svg"), "globe": require("./globe.svg"),
"magnifying-glass": require("./magnifying-glass.svg"), "magnifying-glass": require("./magnifying-glass.svg"),
"pause": require("./pause.svg"), "pause": require("./pause.svg"),
"pause-circle": require("./pause-circle.svg"),
"pause-exceptions": require("./pause-exceptions.svg"), "pause-exceptions": require("./pause-exceptions.svg"),
"prettyPrint": require("./prettyPrint.svg"), "prettyPrint": require("./prettyPrint.svg"),
"resume": require("./resume.svg"), "resume": require("./resume.svg"),

View File

@ -1,6 +1,6 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public <!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this - License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. --> - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="33" height="12" viewBox="0 0 33 12"> <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 60 12">
<path id="base-path" d="M27.1,0H1C0.4,0,0,0.4,0,1v10c0,0.6,0.4,1,1,1h26.1 c0.6,0,1.2-0.3,1.5-0.7L33,6l-4.4-5.3C28.2,0.3,27.7,0,27.1,0z"/> <path id="base-path" d="M53.9,0H1C0.4,0,0,0.4,0,1v10c0,0.6,0.4,1,1,1h52.9c0.6,0,1.2-0.3,1.5-0.7L60,6l-4.4-5.3C55,0.3,54.5,0,53.9,0z"/>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 486 B

After

Width:  |  Height:  |  Size: 460 B

View File

@ -46,7 +46,7 @@ var Debugger =
/***/ 0: /***/ 0:
/***/ function(module, exports, __webpack_require__) { /***/ function(module, exports, __webpack_require__) {
var prettyFast = __webpack_require__(366); var prettyFast = __webpack_require__(438);
self.onmessage = function (msg) { self.onmessage = function (msg) {
var _prettyPrint = prettyPrint(msg.data); var _prettyPrint = prettyPrint(msg.data);
@ -100,7 +100,7 @@ var Debugger =
/***/ }, /***/ },
/***/ 366: /***/ 438:
/***/ function(module, exports, __webpack_require__) { /***/ function(module, exports, __webpack_require__) {
var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_RESULT__;/* -*- indent-tabs-mode: nil; js-indent-level: 2; fill-column: 80 -*- */ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_RESULT__;/* -*- indent-tabs-mode: nil; js-indent-level: 2; fill-column: 80 -*- */
@ -122,8 +122,8 @@ var Debugger =
}(this, function () { }(this, function () {
"use strict"; "use strict";
var acorn = this.acorn || __webpack_require__(367); var acorn = this.acorn || __webpack_require__(439);
var sourceMap = this.sourceMap || __webpack_require__(368); var sourceMap = this.sourceMap || __webpack_require__(440);
var SourceNode = sourceMap.SourceNode; var SourceNode = sourceMap.SourceNode;
// If any of these tokens are seen before a "[" token, we know that "[" token // If any of these tokens are seen before a "[" token, we know that "[" token
@ -982,7 +982,7 @@ var Debugger =
/***/ }, /***/ },
/***/ 367: /***/ 439:
/***/ function(module, exports, __webpack_require__) { /***/ function(module, exports, __webpack_require__) {
var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;// Acorn is a tiny, fast JavaScript parser written in JavaScript. var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;// Acorn is a tiny, fast JavaScript parser written in JavaScript.
@ -3642,7 +3642,7 @@ var Debugger =
/***/ }, /***/ },
/***/ 368: /***/ 440:
/***/ function(module, exports, __webpack_require__) { /***/ function(module, exports, __webpack_require__) {
/* /*
@ -3650,14 +3650,14 @@ var Debugger =
* Licensed under the New BSD license. See LICENSE.txt or: * Licensed under the New BSD license. See LICENSE.txt or:
* http://opensource.org/licenses/BSD-3-Clause * http://opensource.org/licenses/BSD-3-Clause
*/ */
exports.SourceMapGenerator = __webpack_require__(369).SourceMapGenerator; exports.SourceMapGenerator = __webpack_require__(441).SourceMapGenerator;
exports.SourceMapConsumer = __webpack_require__(375).SourceMapConsumer; exports.SourceMapConsumer = __webpack_require__(447).SourceMapConsumer;
exports.SourceNode = __webpack_require__(377).SourceNode; exports.SourceNode = __webpack_require__(449).SourceNode;
/***/ }, /***/ },
/***/ 369: /***/ 441:
/***/ function(module, exports, __webpack_require__) { /***/ function(module, exports, __webpack_require__) {
var __WEBPACK_AMD_DEFINE_RESULT__;/* -*- Mode: js; js-indent-level: 2; -*- */ var __WEBPACK_AMD_DEFINE_RESULT__;/* -*- Mode: js; js-indent-level: 2; -*- */
@ -3671,10 +3671,10 @@ var Debugger =
} }
!(__WEBPACK_AMD_DEFINE_RESULT__ = function (require, exports, module) { !(__WEBPACK_AMD_DEFINE_RESULT__ = function (require, exports, module) {
var base64VLQ = __webpack_require__(370); var base64VLQ = __webpack_require__(442);
var util = __webpack_require__(372); var util = __webpack_require__(444);
var ArraySet = __webpack_require__(373).ArraySet; var ArraySet = __webpack_require__(445).ArraySet;
var MappingList = __webpack_require__(374).MappingList; var MappingList = __webpack_require__(446).MappingList;
/** /**
* An instance of the SourceMapGenerator represents a source map which is * An instance of the SourceMapGenerator represents a source map which is
@ -4064,7 +4064,7 @@ var Debugger =
/***/ }, /***/ },
/***/ 370: /***/ 442:
/***/ function(module, exports, __webpack_require__) { /***/ function(module, exports, __webpack_require__) {
var __WEBPACK_AMD_DEFINE_RESULT__;/* -*- Mode: js; js-indent-level: 2; -*- */ var __WEBPACK_AMD_DEFINE_RESULT__;/* -*- Mode: js; js-indent-level: 2; -*- */
@ -4108,7 +4108,7 @@ var Debugger =
} }
!(__WEBPACK_AMD_DEFINE_RESULT__ = function (require, exports, module) { !(__WEBPACK_AMD_DEFINE_RESULT__ = function (require, exports, module) {
var base64 = __webpack_require__(371); var base64 = __webpack_require__(443);
// A single base 64 digit can contain 6 bits of data. For the base 64 variable // A single base 64 digit can contain 6 bits of data. For the base 64 variable
// length quantities we use in the source map spec, the first bit is the sign, // length quantities we use in the source map spec, the first bit is the sign,
@ -4213,7 +4213,7 @@ var Debugger =
/***/ }, /***/ },
/***/ 371: /***/ 443:
/***/ function(module, exports, __webpack_require__) { /***/ function(module, exports, __webpack_require__) {
var __WEBPACK_AMD_DEFINE_RESULT__;/* -*- Mode: js; js-indent-level: 2; -*- */ var __WEBPACK_AMD_DEFINE_RESULT__;/* -*- Mode: js; js-indent-level: 2; -*- */
@ -4262,7 +4262,7 @@ var Debugger =
/***/ }, /***/ },
/***/ 372: /***/ 444:
/***/ function(module, exports, __webpack_require__) { /***/ function(module, exports, __webpack_require__) {
var __WEBPACK_AMD_DEFINE_RESULT__;/* -*- Mode: js; js-indent-level: 2; -*- */ var __WEBPACK_AMD_DEFINE_RESULT__;/* -*- Mode: js; js-indent-level: 2; -*- */
@ -4588,7 +4588,7 @@ var Debugger =
/***/ }, /***/ },
/***/ 373: /***/ 445:
/***/ function(module, exports, __webpack_require__) { /***/ function(module, exports, __webpack_require__) {
var __WEBPACK_AMD_DEFINE_RESULT__;/* -*- Mode: js; js-indent-level: 2; -*- */ var __WEBPACK_AMD_DEFINE_RESULT__;/* -*- Mode: js; js-indent-level: 2; -*- */
@ -4602,7 +4602,7 @@ var Debugger =
} }
!(__WEBPACK_AMD_DEFINE_RESULT__ = function (require, exports, module) { !(__WEBPACK_AMD_DEFINE_RESULT__ = function (require, exports, module) {
var util = __webpack_require__(372); var util = __webpack_require__(444);
/** /**
* A data structure which is a combination of an array and a set. Adding a new * A data structure which is a combination of an array and a set. Adding a new
@ -4692,7 +4692,7 @@ var Debugger =
/***/ }, /***/ },
/***/ 374: /***/ 446:
/***/ function(module, exports, __webpack_require__) { /***/ function(module, exports, __webpack_require__) {
var __WEBPACK_AMD_DEFINE_RESULT__;/* -*- Mode: js; js-indent-level: 2; -*- */ var __WEBPACK_AMD_DEFINE_RESULT__;/* -*- Mode: js; js-indent-level: 2; -*- */
@ -4706,7 +4706,7 @@ var Debugger =
} }
!(__WEBPACK_AMD_DEFINE_RESULT__ = function (require, exports, module) { !(__WEBPACK_AMD_DEFINE_RESULT__ = function (require, exports, module) {
var util = __webpack_require__(372); var util = __webpack_require__(444);
/** /**
* Determine whether mappingB is after mappingA with respect to generated * Determine whether mappingB is after mappingA with respect to generated
@ -4785,7 +4785,7 @@ var Debugger =
/***/ }, /***/ },
/***/ 375: /***/ 447:
/***/ function(module, exports, __webpack_require__) { /***/ function(module, exports, __webpack_require__) {
var __WEBPACK_AMD_DEFINE_RESULT__;/* -*- Mode: js; js-indent-level: 2; -*- */ var __WEBPACK_AMD_DEFINE_RESULT__;/* -*- Mode: js; js-indent-level: 2; -*- */
@ -4799,10 +4799,10 @@ var Debugger =
} }
!(__WEBPACK_AMD_DEFINE_RESULT__ = function (require, exports, module) { !(__WEBPACK_AMD_DEFINE_RESULT__ = function (require, exports, module) {
var util = __webpack_require__(372); var util = __webpack_require__(444);
var binarySearch = __webpack_require__(376); var binarySearch = __webpack_require__(448);
var ArraySet = __webpack_require__(373).ArraySet; var ArraySet = __webpack_require__(445).ArraySet;
var base64VLQ = __webpack_require__(370); var base64VLQ = __webpack_require__(442);
/** /**
* A SourceMapConsumer instance represents a parsed source map which we can * A SourceMapConsumer instance represents a parsed source map which we can
@ -5367,7 +5367,7 @@ var Debugger =
/***/ }, /***/ },
/***/ 376: /***/ 448:
/***/ function(module, exports, __webpack_require__) { /***/ function(module, exports, __webpack_require__) {
var __WEBPACK_AMD_DEFINE_RESULT__;/* -*- Mode: js; js-indent-level: 2; -*- */ var __WEBPACK_AMD_DEFINE_RESULT__;/* -*- Mode: js; js-indent-level: 2; -*- */
@ -5454,7 +5454,7 @@ var Debugger =
/***/ }, /***/ },
/***/ 377: /***/ 449:
/***/ function(module, exports, __webpack_require__) { /***/ function(module, exports, __webpack_require__) {
var __WEBPACK_AMD_DEFINE_RESULT__;/* -*- Mode: js; js-indent-level: 2; -*- */ var __WEBPACK_AMD_DEFINE_RESULT__;/* -*- Mode: js; js-indent-level: 2; -*- */
@ -5468,8 +5468,8 @@ var Debugger =
} }
!(__WEBPACK_AMD_DEFINE_RESULT__ = function (require, exports, module) { !(__WEBPACK_AMD_DEFINE_RESULT__ = function (require, exports, module) {
var SourceMapGenerator = __webpack_require__(369).SourceMapGenerator; var SourceMapGenerator = __webpack_require__(441).SourceMapGenerator;
var util = __webpack_require__(372); var util = __webpack_require__(444);
// Matches a Windows-style `\r\n` newline or a `\n` newline used by all other // Matches a Windows-style `\r\n` newline or a `\n` newline used by all other
// operating systems these days (capturing the result). // operating systems these days (capturing the result).

View File

@ -63,13 +63,17 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this file, * License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */ * You can obtain one at http://mozilla.org/MPL/2.0/. */
:root.theme-light,
:root .theme-light {
--theme-search-overlays-semitransparent: rgba(221, 225, 228, 0.66);
}
* { * {
box-sizing: border-box; box-sizing: border-box;
} }
html, html,
body { body {
font-family: "SF UI Text", sans-serif;
height: 100%; height: 100%;
margin: 0; margin: 0;
padding: 0; padding: 0;
@ -102,30 +106,6 @@ body {
cursor: pointer; cursor: pointer;
} }
.source-footer {
background: var(--theme-body-background);
position: absolute;
bottom: 0;
right: 0;
z-index: 100;
width: 100px;
opacity: 0.9;
}
.source-footer .command-bar {
float: right;
}
.command-bar > span {
cursor: pointer;
margin-right: 0.7em;
width: 1em;
height: 1.1em;
display: inline-block;
text-align: center;
transition: opacity 200ms;
}
.search-container { .search-container {
position: absolute; position: absolute;
top: 0; top: 0;
@ -146,6 +126,94 @@ body {
margin-top: 25px; margin-top: 25px;
margin-right: 20px; margin-right: 20px;
} }
/* vim:set ts=2 sw=2 sts=2 et: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
.split-box {
display: flex;
flex: 1;
min-width: 0;
height: 100%;
width: 100%;
}
.split-box.vert {
flex-direction: row;
}
.split-box.horz {
flex-direction: column;
}
.split-box > .uncontrolled {
display: flex;
flex: 1;
min-width: 0;
overflow: auto;
}
.split-box > .controlled {
display: flex;
overflow: auto;
}
.split-box > .splitter {
background-image: none;
border: 0;
border-style: solid;
border-color: transparent;
background-color: var(--theme-splitter-color);
background-clip: content-box;
position: relative;
box-sizing: border-box;
/* Positive z-index positions the splitter on top of its siblings and makes
it clickable on both sides. */
z-index: 1;
}
.split-box.vert > .splitter {
min-width: calc(var(--devtools-splitter-inline-start-width) +
var(--devtools-splitter-inline-end-width) + 1px);
border-left-width: var(--devtools-splitter-inline-start-width);
border-right-width: var(--devtools-splitter-inline-end-width);
margin-left: calc(-1 * var(--devtools-splitter-inline-start-width) - 1px);
margin-right: calc(-1 * var(--devtools-splitter-inline-end-width));
cursor: ew-resize;
}
.split-box.horz > .splitter {
min-height: calc(var(--devtools-splitter-top-width) +
var(--devtools-splitter-bottom-width) + 1px);
border-top-width: var(--devtools-splitter-top-width);
border-bottom-width: var(--devtools-splitter-bottom-width);
margin-top: calc(-1 * var(--devtools-splitter-top-width) - 1px);
margin-bottom: calc(-1 * var(--devtools-splitter-bottom-width));
cursor: ns-resize;
}
.split-box.disabled {
pointer-events: none;
}
/**
* Make sure splitter panels are not processing any mouse
* events. This is good for performance during splitter
* bar dragging.
*/
.split-box.dragging > .controlled,
.split-box.dragging > .uncontrolled {
pointer-events: none;
}
.tree { .tree {
-webkit-user-select: none; -webkit-user-select: none;
-moz-user-select: none; -moz-user-select: none;
@ -193,9 +261,19 @@ body {
.sources-header { .sources-header {
height: 30px; height: 30px;
border-bottom: 1px solid var(--theme-splitter-color); border-bottom: 1px solid var(--theme-splitter-color);
padding-left: 10px; padding: 0 10px;
line-height: 30px; line-height: 30px;
font-size: 1.2em; font-size: 1.2em;
display: flex;
align-items: baseline;
justify-content: space-between;
}
.sources-header-info {
font-size: 0.7em;
color: var(--theme-comment-alt);
font-weight: lighter;
white-space: nowrap;
} }
.sources-list { .sources-list {
@ -313,6 +391,49 @@ ul.sources-list {
.sources-list .tree-node button { .sources-list .tree-node button {
position: fixed; position: fixed;
} }
.source-footer {
background: var(--theme-body-background);
border-top: 1px solid var(--theme-splitter-color);
position: absolute;
bottom: 0;
left: 0;
right: 0;
opacity: 1;
z-index: 100;
}
.source-footer .command-bar {
float: right;
}
.command-bar > span {
cursor: pointer;
margin-right: 0.7em;
width: 1em;
height: 1.1em;
display: inline-block;
text-align: center;
transition: opacity 200ms;
}
.source-footer .prettyPrint.pretty {
stroke: var(--theme-highlight-blue);
}
.source-footer input:focus {
border-color: var(--theme-highlight-blue);
outline: none;
}
.source-footer input {
line-height: 16px;
margin: 7px;
border-radius: 2px;
border: 1px solid var(--theme-splitter-color);
padding-left: 4px;
font-size: 10px;
}
/* vim:set ts=2 sw=2 sts=2 et: */ /* vim:set ts=2 sw=2 sts=2 et: */
/* This Source Code Form is subject to the terms of the Mozilla Public /* This Source Code Form is subject to the terms of the Mozilla Public
@ -325,7 +446,7 @@ ul.sources-list {
*/ */
.editor-wrapper { .editor-wrapper {
position: absolute; position: absolute;
height: calc(100% - 30px); height: calc(100% - 31px);
width: 100%; width: 100%;
top: 30px; top: 30px;
left: 0px; left: 0px;
@ -391,7 +512,7 @@ ul.sources-list {
} }
.welcomebox { .welcomebox {
width: 100%; width: calc(100% - 1px);
/* Offsetting it by 30px for the sources-header area */ /* Offsetting it by 30px for the sources-header area */
height: calc(100% - 30px); height: calc(100% - 30px);
@ -404,45 +525,33 @@ ul.sources-list {
color: var(--theme-comment-alt); color: var(--theme-comment-alt);
background-color: var(--theme-tab-toolbar-background); background-color: var(--theme-tab-toolbar-background);
font-weight: lighter; font-weight: lighter;
text-align: center;
z-index: 100; z-index: 100;
} }
.split-box { .close-btn path {
display: flex; fill: var(--theme-body-color);
flex: 1;
min-width: 0;
} }
.split-box .uncontrolled { .close-btn .close {
display: flex; width: 12px;
flex: 1; height: 12px;
min-width: 0; padding: 2px;
overflow: auto; text-align: center;
margin-top: 2px;
line-height: 5px;
transition: all 0.25s easeinout;
} }
.split-box .controlled { .close-btn .close svg {
display: flex; width: 6px;
overflow: auto;
} }
.split-box .splitter { .close-btn .close:hover {
background-color: var(--theme-splitter-color); background: var(--theme-selection-background);
flex: 0 0 1px; border-radius: 2px;
position: relative;
} }
/* The actual handle that users drag is a transparent element that's slightly wider than .close-btn .close:hover path {
the splitter element itself, so it's easier to grab it. */ fill: white;
.split-box .splitter .splitter-handle {
cursor: ew-resize;
position: absolute;
top: 0;
left: -4px;
width: 8px;
height: 100%;
/* Stack above the code-mirror editor so it's actually possible to grab the handle. */
z-index: 5;
} }
.breakpoints-list .breakpoint { .breakpoints-list .breakpoint {
@ -451,6 +560,7 @@ ul.sources-list {
margin: 0.25em 0; margin: 0.25em 0;
padding: 0.25em 0; padding: 0.25em 0;
line-height: 1em; line-height: 1em;
position: relative;
} }
.breakpoints-list .breakpoint.paused { .breakpoints-list .breakpoint.paused {
@ -480,6 +590,20 @@ ul.sources-list {
color: var(--theme-comment); color: var(--theme-comment);
padding-left: 20px; padding-left: 20px;
} }
.breakpoint .close-btn {
position: absolute;
right: 6px;
top: 6px;
}
.breakpoint .close {
display: none;
}
.breakpoint:hover .close {
display: block;
}
/* vim:set ts=2 sw=2 sts=2 et: */ /* vim:set ts=2 sw=2 sts=2 et: */
/* This Source Code Form is subject to the terms of the Mozilla Public /* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this
@ -812,10 +936,14 @@ ul.sources-list {
.right-sidebar .accordion { .right-sidebar .accordion {
overflow-y: auto; overflow-y: auto;
overflow-x: hidden;
}
.right-sidebar .command-bar {
border-bottom: 1px solid var(--theme-splitter-color);
} }
.command-bar { .command-bar {
border-bottom: 1px solid var(--theme-splitter-color);
height: 30px; height: 30px;
padding: 8px 5px 10px 10px; padding: 8px 5px 10px 10px;
} }
@ -856,6 +984,14 @@ ul.sources-list {
.disableBreakpoints.breakpoints-disabled path { .disableBreakpoints.breakpoints-disabled path {
stroke: var(--theme-highlight-blue); stroke: var(--theme-highlight-blue);
} }
span.pause-exceptions.uncaught {
stroke: var(--theme-highlight-purple);
}
span.pause-exceptions.all {
stroke: var(--theme-highlight-blue);
}
.source-header { .source-header {
border-bottom: 1px solid var(--theme-splitter-color); border-bottom: 1px solid var(--theme-splitter-color);
height: 30px; height: 30px;
@ -891,6 +1027,7 @@ ul.sources-list {
.source-tab.active { .source-tab.active {
color: var(--theme-body-color); color: var(--theme-body-color);
background-color: var(--theme-body-background); background-color: var(--theme-body-background);
height: 24px;
} }
.source-tab path { .source-tab path {
@ -903,10 +1040,16 @@ ul.sources-list {
.source-tab .close-btn { .source-tab .close-btn {
position: absolute; position: absolute;
right: 7px; right: 4px;
top: 1px; top: 3px;
width: 6px; }
height: 6px;
.source-tab .close {
display: none;
}
.source-tab:hover .close {
display: block;
} }
.source-header .subsettings { .source-header .subsettings {
@ -944,16 +1087,6 @@ ul.sources-list {
margin: 0; margin: 0;
padding: 0; padding: 0;
} }
.source-footer {
border-top: 1px solid var(--theme-splitter-color);
left: 0;
opacity: 1;
width: 100%;
}
.source-footer .prettyPrint.pretty {
stroke: var(--theme-highlight-blue);
}
.autocomplete { .autocomplete {
width: 100%; width: 100%;

View File

@ -5,17 +5,24 @@ support-files =
head.js head.js
!/devtools/client/commandline/test/helpers.js !/devtools/client/commandline/test/helpers.js
!/devtools/client/framework/test/shared-head.js !/devtools/client/framework/test/shared-head.js
examples/bundle.js
examples/bundle.js.map
examples/doc-scripts.html examples/doc-scripts.html
examples/doc-script-switching.html examples/doc-script-switching.html
examples/doc-exceptions.html examples/doc-exceptions.html
examples/doc-iframes.html examples/doc-iframes.html
examples/doc-debugger-statements.html examples/doc-debugger-statements.html
examples/code-exceptions.js examples/doc-sourcemaps.html
examples/code-simple1.js examples/entry.js
examples/code-simple2.js examples/exceptions.js
examples/code-long.js examples/opts.js
examples/code-script-switching-02.js examples/long.js
examples/code-script-switching-01.js examples/output.js
examples/simple1.js
examples/simple2.js
examples/script-switching-02.js
examples/script-switching-01.js
examples/times2.js
[browser_dbg-breaking.js] [browser_dbg-breaking.js]
[browser_dbg-breaking-from-console.js] [browser_dbg-breaking-from-console.js]

View File

@ -23,8 +23,7 @@ add_task(function* () {
yield paused; yield paused;
yield resume(dbg); yield resume(dbg);
const source = getSelectedSource(getState()).toJS(); const source = getSelectedSource(getState()).toJS();
// TODO: The url of an eval source should be null. ok(!source.url, "It is an eval source");
ok(source.url.indexOf("SOURCE") === 0, "It is an eval source");
yield addBreakpoint(dbg, source, 5); yield addBreakpoint(dbg, source, 5);
invokeInTab("evaledFunc"); invokeInTab("evaledFunc");

View File

@ -8,7 +8,7 @@
add_task(function* () { add_task(function* () {
const dbg = yield initDebugger("doc-scripts.html"); const dbg = yield initDebugger("doc-scripts.html");
const { selectors: { getSourceText }, getState } = dbg; const { selectors: { getSourceText }, getState } = dbg;
const sourceUrl = EXAMPLE_URL + "code-long.js"; const sourceUrl = EXAMPLE_URL + "long.js";
// The source itself doesn't even exist yet, and using // The source itself doesn't even exist yet, and using
// `selectSourceURL` will set a pending request to load this source // `selectSourceURL` will set a pending request to load this source

View File

@ -12,6 +12,11 @@ function isElementVisible(dbg, elementName) {
} }
add_task(function* () { add_task(function* () {
// This test runs too slowly on linux debug. I'd like to figure out
// which is the slowest part of this and make it run faster, but to
// fix a frequent failure allow a longer timeout.
requestLongerTimeout(2);
const dbg = yield initDebugger( const dbg = yield initDebugger(
"doc-scripts.html", "doc-scripts.html",
"simple1.js", "simple2.js", "long.js" "simple1.js", "simple2.js", "long.js"

View File

@ -0,0 +1,15 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
// Tests sourcemaps.
add_task(function* () {
const dbg = yield initDebugger("doc-sourcemaps.html");
yield waitForSources(dbg, "entry.js", "output.js", "times2.js", "opts.js");
ok(true, "Original sources exist");
yield selectSource(dbg, "output.js");
ok(dbg.win.cm.getValue().includes("function output"),
"Original source text loaded correctly");
});

View File

@ -0,0 +1,90 @@
/******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId])
/******/ return installedModules[moduleId].exports;
/******/
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ exports: {},
/******/ id: moduleId,
/******/ loaded: false
/******/ };
/******/
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ // Flag the module as loaded
/******/ module.loaded = true;
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/******/
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/
/******/ // Load entry module and return exports
/******/ return __webpack_require__(0);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ function(module, exports, __webpack_require__) {
const times2 = __webpack_require__(1);
const { output } = __webpack_require__(2);
const opts = __webpack_require__(3);
output(times2(1));
output(times2(2));
if(opts.extra) {
output(times2(3));
}
/***/ },
/* 1 */
/***/ function(module, exports) {
module.exports = function(x) {
return x * 2;
}
/***/ },
/* 2 */
/***/ function(module, exports) {
function output(str) {
console.log(str);
}
module.exports = { output };
/***/ },
/* 3 */
/***/ function(module, exports) {
module.exports = {
extra: true
};
/***/ }
/******/ ]);
//# sourceMappingURL=bundle.js.map

View File

@ -0,0 +1 @@
{"version":3,"sources":["webpack:///webpack/bootstrap 54b46cf0214c369e95aa","webpack:///./entry.js","webpack:///./times2.js","webpack:///./output.js","webpack:///./opts.js"],"names":[],"mappings":";AAAA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA,uBAAe;AACf;AACA;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;;;AAGA;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;;;;;;;ACtCA;AACA,QAAO,SAAS;AAChB;;AAEA;AACA;;AAEA;AACA;AACA;;;;;;;ACTA;AACA;AACA;;;;;;;ACFA;AACA;AACA;;AAEA,mBAAkB;;;;;;;ACJlB;AACA;AACA","file":"bundle.js","sourcesContent":[" \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId])\n \t\t\treturn installedModules[moduleId].exports;\n\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\texports: {},\n \t\t\tid: moduleId,\n \t\t\tloaded: false\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.loaded = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"\";\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(0);\n\n\n\n/** WEBPACK FOOTER **\n ** webpack/bootstrap 54b46cf0214c369e95aa\n **/","const times2 = require(\"./times2\");\nconst { output } = require(\"./output\");\nconst opts = require(\"./opts\");\n\noutput(times2(1));\noutput(times2(2));\n\nif(opts.extra) {\n output(times2(3));\n}\n\n\n\n/*****************\n ** WEBPACK FOOTER\n ** ./entry.js\n ** module id = 0\n ** module chunks = 0\n **/","module.exports = function(x) {\n return x * 2;\n}\n\n\n\n/*****************\n ** WEBPACK FOOTER\n ** ./times2.js\n ** module id = 1\n ** module chunks = 0\n **/","function output(str) {\n console.log(str);\n}\n\nmodule.exports = { output };\n\n\n\n/*****************\n ** WEBPACK FOOTER\n ** ./output.js\n ** module id = 2\n ** module chunks = 0\n **/","module.exports = {\n extra: true\n};\n\n\n\n/*****************\n ** WEBPACK FOOTER\n ** ./opts.js\n ** module id = 3\n ** module chunks = 0\n **/"],"sourceRoot":""}

View File

@ -1,7 +1,7 @@
<html> <html>
<head> <head>
<title>Debugger test page</title> <title>Debugger test page</title>
<script type="text/javascript" src="code-exceptions.js"></script> <script type="text/javascript" src="exceptions.js"></script>
</head> </head>
<body></body> <body></body>
</html> </html>

View File

@ -11,8 +11,8 @@
<body> <body>
<button onclick="firstCall()">Click me!</button> <button onclick="firstCall()">Click me!</button>
<script type="text/javascript" src="code-script-switching-01.js"></script> <script type="text/javascript" src="script-switching-01.js"></script>
<script type="text/javascript" src="code-script-switching-02.js"></script> <script type="text/javascript" src="script-switching-02.js"></script>
</body> </body>
</html> </html>

View File

@ -8,9 +8,9 @@
</head> </head>
<body> <body>
<script src="code-simple1.js"></script> <script src="simple1.js"></script>
<script src="code-simple2.js"></script> <script src="simple2.js"></script>
<script src="code-long.js"></script> <script src="long.js"></script>
<script> <script>
// This inline script allows this HTML page to show up as a // This inline script allows this HTML page to show up as a
// source. It also needs to introduce a new global variable so // source. It also needs to introduce a new global variable so

View File

@ -0,0 +1,13 @@
<!-- Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ -->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title>Debugger test page</title>
</head>
<body>
<script src="bundle.js"></script>
</body>
</html>

View File

@ -0,0 +1,10 @@
const times2 = require("./times2");
const { output } = require("./output");
const opts = require("./opts");
output(times2(1));
output(times2(2));
if(opts.extra) {
output(times2(3));
}

View File

@ -0,0 +1,3 @@
module.exports = {
extra: true
};

View File

@ -0,0 +1,5 @@
function output(str) {
console.log(str);
}
module.exports = { output };

View File

@ -0,0 +1,3 @@
module.exports = function(x) {
return x * 2;
}

View File

@ -0,0 +1,8 @@
module.exports = {
entry: "./entry.js",
output: {
filename: "bundle.js"
},
devtool: "sourcemap"
}

View File

@ -5,6 +5,35 @@
"use strict"; "use strict";
/**
* The Mochitest API documentation
* @module mochitest
*/
/**
* The mochitest API to wait for certain events.
* @module mochitest/waits
* @parent mochitest
*/
/**
* The mochitest API predefined asserts.
* @module mochitest/asserts
* @parent mochitest
*/
/**
* The mochitest API for interacting with the debugger.
* @module mochitest/actions
* @parent mochitest
*/
/**
* Helper methods for the mochitest API.
* @module mochitest/helpers
* @parent mochitest
*/
// shared-head.js handles imports, constants, and utility functions // shared-head.js handles imports, constants, and utility functions
Services.scriptloader.loadSubScript("chrome://mochitests/content/browser/devtools/client/framework/test/shared-head.js", this); Services.scriptloader.loadSubScript("chrome://mochitests/content/browser/devtools/client/framework/test/shared-head.js", this);
var { Toolbox } = require("devtools/client/framework/toolbox"); var { Toolbox } = require("devtools/client/framework/toolbox");
@ -58,6 +87,17 @@ function _afterDispatchDone(store, type) {
}); });
} }
/**
* Wait for a specific action type to be dispatch.
* If an async action, will wait for it to be done.
*
* @memberof mochitest/waits
* @param {Object} dbg
* @param {String} type
* @param {Number} eventRepeat
* @return {Promise}
* @static
*/
function waitForDispatch(dbg, type, eventRepeat = 1) { function waitForDispatch(dbg, type, eventRepeat = 1) {
let count = 0; let count = 0;
@ -71,6 +111,15 @@ function waitForDispatch(dbg, type, eventRepeat = 1) {
}); });
} }
/**
* Waits for specific thread events.
*
* @memberof mochitest/waits
* @param {Object} dbg
* @param {String} eventName
* @return {Promise}
* @static
*/
function waitForThreadEvents(dbg, eventName) { function waitForThreadEvents(dbg, eventName) {
info("Waiting for thread event '" + eventName + "' to fire."); info("Waiting for thread event '" + eventName + "' to fire.");
const thread = dbg.toolbox.threadClient; const thread = dbg.toolbox.threadClient;
@ -84,6 +133,15 @@ function waitForThreadEvents(dbg, eventName) {
}); });
} }
/**
* Waits for `predicate(state)` to be true. `state` is the redux app state.
*
* @memberof mochitest/waits
* @param {Object} dbg
* @param {Function} predicate
* @return {Promise}
* @static
*/
function waitForState(dbg, predicate) { function waitForState(dbg, predicate) {
return new Promise(resolve => { return new Promise(resolve => {
const unsubscribe = dbg.store.subscribe(() => { const unsubscribe = dbg.store.subscribe(() => {
@ -95,6 +153,15 @@ function waitForState(dbg, predicate) {
}); });
} }
/**
* Waits for sources to be loaded.
*
* @memberof mochitest/waits
* @param {Object} dbg
* @param {Array} sources
* @return {Promise}
* @static
*/
function waitForSources(dbg, ...sources) { function waitForSources(dbg, ...sources) {
if(sources.length === 0) { if(sources.length === 0) {
return Promise.resolve(); return Promise.resolve();
@ -104,7 +171,9 @@ function waitForSources(dbg, ...sources) {
const {selectors: {getSources}, store} = dbg; const {selectors: {getSources}, store} = dbg;
return Promise.all(sources.map(url => { return Promise.all(sources.map(url => {
function sourceExists(state) { function sourceExists(state) {
return getSources(state).some(s => s.get("url").includes(url)); return getSources(state).some(s => {
return s.get("url").includes(url)
});
} }
if(!sourceExists(store.getState())) { if(!sourceExists(store.getState())) {
@ -113,6 +182,15 @@ function waitForSources(dbg, ...sources) {
})); }));
} }
/**
* Assert that the debugger is paused at the correct location.
*
* @memberof mochitest/asserts
* @param {Object} dbg
* @param {String} source
* @param {Number} line
* @static
*/
function assertPausedLocation(dbg, source, line) { function assertPausedLocation(dbg, source, line) {
const { selectors: { getSelectedSource, getPause }, getState } = dbg; const { selectors: { getSelectedSource, getPause }, getState } = dbg;
source = findSource(dbg, source); source = findSource(dbg, source);
@ -130,6 +208,15 @@ function assertPausedLocation(dbg, source, line) {
"Line is highlighted as paused"); "Line is highlighted as paused");
} }
/**
* Assert that the debugger is highlighting the correct location.
*
* @memberof mochitest/asserts
* @param {Object} dbg
* @param {String} source
* @param {Number} line
* @static
*/
function assertHighlightLocation(dbg, source, line) { function assertHighlightLocation(dbg, source, line) {
const { selectors: { getSelectedSource, getPause }, getState } = dbg; const { selectors: { getSelectedSource, getPause }, getState } = dbg;
source = findSource(dbg, source); source = findSource(dbg, source);
@ -146,11 +233,25 @@ function assertHighlightLocation(dbg, source, line) {
"Line is highlighted"); "Line is highlighted");
} }
/**
* Returns boolean for whether the debugger is paused.
*
* @memberof mochitest/asserts
* @param {Object} dbg
* @static
*/
function isPaused(dbg) { function isPaused(dbg) {
const { selectors: { getPause }, getState } = dbg; const { selectors: { getPause }, getState } = dbg;
return !!getPause(getState()); return !!getPause(getState());
} }
/**
* Waits for the debugger to be fully paused.
*
* @memberof mochitest/waits
* @param {Object} dbg
* @static
*/
function waitForPaused(dbg) { function waitForPaused(dbg) {
return Task.spawn(function* () { return Task.spawn(function* () {
// We want to make sure that we get both a real paused event and // We want to make sure that we get both a real paused event and
@ -187,6 +288,15 @@ function createDebuggerContext(toolbox) {
}; };
} }
/**
* Intilializes the debugger.
*
* @memberof mochitest
* @param {String} url
* @param {Array} sources
* @return {Promise} dbg
* @static
*/
function initDebugger(url, ...sources) { function initDebugger(url, ...sources) {
return Task.spawn(function* () { return Task.spawn(function* () {
const toolbox = yield openNewTabAndToolbox(EXAMPLE_URL + url, "jsdebugger"); const toolbox = yield openNewTabAndToolbox(EXAMPLE_URL + url, "jsdebugger");
@ -197,13 +307,28 @@ function initDebugger(url, ...sources) {
}; };
window.resumeTest = undefined; window.resumeTest = undefined;
/**
* Pause the test and let you interact with the debugger.
* The test can be resumed by invoking `resumeTest` in the console.
*
* @memberof mochitest
* @static
*/
function pauseTest() { function pauseTest() {
info("Test paused. Invoke resumeTest to continue."); info("Test paused. Invoke resumeTest to continue.");
return new Promise(resolve => resumeTest = resolve); return new Promise(resolve => resumeTest = resolve);
} }
// Actions // Actions
/**
* Returns a source that matches the URL.
*
* @memberof mochitest/actions
* @param {Object} dbg
* @param {String} url
* @return {Object} source
* @static
*/
function findSource(dbg, url) { function findSource(dbg, url) {
if(typeof url !== "string") { if(typeof url !== "string") {
// Support passing in a source object itelf all APIs that use this // Support passing in a source object itelf all APIs that use this
@ -222,6 +347,16 @@ function findSource(dbg, url) {
return source.toJS(); return source.toJS();
} }
/**
* Selects the source.
*
* @memberof mochitest/actions
* @param {Object} dbg
* @param {String} url
* @param {Number} line
* @return {Promise}
* @static
*/
function selectSource(dbg, url, line) { function selectSource(dbg, url, line) {
info("Selecting source: " + url); info("Selecting source: " + url);
const source = findSource(dbg, url); const source = findSource(dbg, url);
@ -233,49 +368,132 @@ function selectSource(dbg, url, line) {
} }
} }
/**
* Steps over.
*
* @memberof mochitest/actions
* @param {Object} dbg
* @return {Promise}
* @static
*/
function stepOver(dbg) { function stepOver(dbg) {
info("Stepping over"); info("Stepping over");
dbg.actions.stepOver(); dbg.actions.stepOver();
return waitForPaused(dbg); return waitForPaused(dbg);
} }
/**
* Steps in.
*
* @memberof mochitest/actions
* @param {Object} dbg
* @return {Promise}
* @static
*/
function stepIn(dbg) { function stepIn(dbg) {
info("Stepping in"); info("Stepping in");
dbg.actions.stepIn(); dbg.actions.stepIn();
return waitForPaused(dbg); return waitForPaused(dbg);
} }
/**
* Steps out.
*
* @memberof mochitest/actions
* @param {Object} dbg
* @return {Promise}
* @static
*/
function stepOut(dbg) { function stepOut(dbg) {
info("Stepping out"); info("Stepping out");
dbg.actions.stepOut(); dbg.actions.stepOut();
return waitForPaused(dbg); return waitForPaused(dbg);
} }
/**
* Resumes.
*
* @memberof mochitest/actions
* @param {Object} dbg
* @return {Promise}
* @static
*/
function resume(dbg) { function resume(dbg) {
info("Resuming"); info("Resuming");
dbg.actions.resume(); dbg.actions.resume();
return waitForThreadEvents(dbg, "resumed"); return waitForThreadEvents(dbg, "resumed");
} }
/**
* Reloads the debuggee.
*
* @memberof mochitest/actions
* @param {Object} dbg
* @param {Array} sources
* @return {Promise}
* @static
*/
function reload(dbg, ...sources) { function reload(dbg, ...sources) {
return dbg.client.reload().then(() => waitForSources(...sources)); return dbg.client.reload().then(() => waitForSources(...sources));
} }
/**
* Navigates the debuggee to another url.
*
* @memberof mochitest/actions
* @param {Object} dbg
* @param {String} url
* @param {Array} sources
* @return {Promise}
* @static
*/
function navigate(dbg, url, ...sources) { function navigate(dbg, url, ...sources) {
dbg.client.navigate(url); dbg.client.navigate(url);
return waitForSources(dbg, ...sources) return waitForSources(dbg, ...sources)
} }
/**
* Adds a breakpoint to a source at line/col.
*
* @memberof mochitest/actions
* @param {Object} dbg
* @param {String} source
* @param {Number} line
* @param {Number} col
* @return {Promise}
* @static
*/
function addBreakpoint(dbg, source, line, col) { function addBreakpoint(dbg, source, line, col) {
source = findSource(dbg, source); source = findSource(dbg, source);
const sourceId = source.id; const sourceId = source.id;
return dbg.actions.addBreakpoint({ sourceId, line, col }); return dbg.actions.addBreakpoint({ sourceId, line, col });
} }
/**
* Removes a breakpoint from a source at line/col.
*
* @memberof mochitest/actions
* @param {Object} dbg
* @param {String} source
* @param {Number} line
* @param {Number} col
* @return {Promise}
* @static
*/
function removeBreakpoint(dbg, sourceId, line, col) { function removeBreakpoint(dbg, sourceId, line, col) {
return dbg.actions.removeBreakpoint({ sourceId, line, col }); return dbg.actions.removeBreakpoint({ sourceId, line, col });
} }
/**
* Toggles the Pause on exceptions feature in the debugger.
*
* @memberof mochitest/actions
* @param {Object} dbg
* @param {Boolean} pauseOnExceptions
* @param {Boolean} ignoreCaughtExceptions
* @return {Promise}
* @static
*/
function togglePauseOnExceptions(dbg, function togglePauseOnExceptions(dbg,
pauseOnExceptions, ignoreCaughtExceptions) { pauseOnExceptions, ignoreCaughtExceptions) {
@ -292,7 +510,15 @@ function togglePauseOnExceptions(dbg,
} }
// Helpers // Helpers
// invoke a global function in the debugged tab
/**
* Invokes a global function in the debuggee tab.
*
* @memberof mochitest/helpers
* @param {String} fnc
* @return {Promise}
* @static
*/
function invokeInTab(fnc) { function invokeInTab(fnc) {
info(`Invoking function ${fnc} in tab`); info(`Invoking function ${fnc} in tab`);
return ContentTask.spawn(gBrowser.selectedBrowser, fnc, function* (fnc) { return ContentTask.spawn(gBrowser.selectedBrowser, fnc, function* (fnc) {
@ -309,6 +535,15 @@ const keyMappings = {
stepOutKey: { code: "VK_F11", modifiers: { ctrlKey: isLinux, shiftKey: true } } stepOutKey: { code: "VK_F11", modifiers: { ctrlKey: isLinux, shiftKey: true } }
}; };
/**
* Simulates a key press in the debugger window.
*
* @memberof mochitest/helpers
* @param {Object} dbg
* @param {String} keyName
* @return {Promise}
* @static
*/
function pressKey(dbg, keyName) { function pressKey(dbg, keyName) {
let keyEvent = keyMappings[keyName]; let keyEvent = keyMappings[keyName];
const { code, modifiers } = keyEvent; const { code, modifiers } = keyEvent;
@ -363,7 +598,16 @@ function findAllElements(dbg, elementName, ...args) {
return dbg.win.document.querySelectorAll(selector); return dbg.win.document.querySelectorAll(selector);
} }
// click an element in the debugger /**
* Simulates a mouse click in the debugger DOM.
*
* @memberof mochitest/helpers
* @param {Object} dbg
* @param {String} elementName
* @param {Array} args
* @return {Promise}
* @static
*/
function clickElement(dbg, elementName, ...args) { function clickElement(dbg, elementName, ...args) {
const selector = getSelector(elementName, ...args); const selector = getSelector(elementName, ...args);
const doc = dbg.win.document; const doc = dbg.win.document;
@ -374,6 +618,14 @@ function clickElement(dbg, elementName, ...args) {
); );
} }
/**
* Toggles the debugger call stack accordian.
*
* @memberof mochitest/actions
* @param {Object} dbg
* @return {Promise}
* @static
*/
function toggleCallStack(dbg) { function toggleCallStack(dbg) {
return findElement(dbg, "callStackHeader").click() return findElement(dbg, "callStackHeader").click()
} }

View File

@ -1263,21 +1263,15 @@ Toolbox.prototype = {
// Prevent flicker while loading by waiting to make visible until now. // Prevent flicker while loading by waiting to make visible until now.
iframe.style.visibility = "visible"; iframe.style.visibility = "visible";
// Try to set the dir attribute as early as possible.
this.setIframeDocumentDir(iframe);
// The build method should return a panel instance, so events can // The build method should return a panel instance, so events can
// be fired with the panel as an argument. However, in order to keep // be fired with the panel as an argument. However, in order to keep
// backward compatibility with existing extensions do a check // backward compatibility with existing extensions do a check
// for a promise return value. // for a promise return value.
let built = definition.build(iframe.contentWindow, this); let built = definition.build(iframe.contentWindow, this);
// Set the dir attribute on the documents of panels using HTML.
let docEl = iframe.contentWindow && iframe.contentWindow.document.documentElement;
if (docEl && docEl.namespaceURI === HTML_NS) {
let top = this.win.top;
let topDocEl = top.document.documentElement;
let isRtl = top.getComputedStyle(topDocEl).direction === "rtl";
docEl.setAttribute("dir", isRtl ? "rtl" : "ltr");
}
if (!(typeof built.then == "function")) { if (!(typeof built.then == "function")) {
let panel = built; let panel = built;
iframe.panel = panel; iframe.panel = panel;
@ -1350,6 +1344,28 @@ Toolbox.prototype = {
return deferred.promise; return deferred.promise;
}, },
/**
* Set the dir attribute on the content document element of the provided iframe.
*
* @param {IFrameElement} iframe
*/
setIframeDocumentDir: function (iframe) {
let docEl = iframe.contentWindow && iframe.contentWindow.document.documentElement;
if (!docEl || docEl.namespaceURI !== HTML_NS) {
// Bail out if the content window or document is not ready or if the document is not
// HTML.
return;
}
if (docEl.hasAttribute("dir")) {
// Set the dir attribute value only if dir is already present on the document.
let top = this.win.top;
let topDocEl = top.document.documentElement;
let isRtl = top.getComputedStyle(topDocEl).direction === "rtl";
docEl.setAttribute("dir", isRtl ? "rtl" : "ltr");
}
},
/** /**
* Mark all in collection as unselected; and id as selected * Mark all in collection as unselected; and id as selected
* @param {string} collection * @param {string} collection

View File

@ -20,7 +20,7 @@
<!DOCTYPE html> <!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"> <html xmlns="http://www.w3.org/1999/xhtml" dir="">
<head> <head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<script type="application/javascript;version=1.8" <script type="application/javascript;version=1.8"

View File

@ -9,7 +9,7 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public <!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this - License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. --> - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<html xmlns="http://www.w3.org/1999/xhtml"> <html xmlns="http://www.w3.org/1999/xhtml" dir="">
<head> <head>
<link rel="stylesheet" href="chrome://devtools/skin/widgets.css" type="text/css"/> <link rel="stylesheet" href="chrome://devtools/skin/widgets.css" type="text/css"/>
<link rel="stylesheet" href="chrome://devtools/skin/memory.css" type="text/css"/> <link rel="stylesheet" href="chrome://devtools/skin/memory.css" type="text/css"/>

View File

@ -122,18 +122,42 @@ function waitForFrameLoad(ui, targetURL) {
} }
function waitForViewportResizeTo(ui, width, height) { function waitForViewportResizeTo(ui, width, height) {
return new Promise(resolve => { return new Promise(Task.async(function* (resolve) {
let isSizeMatching = (data) => data.width == width && data.height == height;
// If the viewport has already the expected size, we resolve the promise immediately.
let size = yield getContentSize(ui);
if (isSizeMatching(size)) {
resolve();
return;
}
// Otherwise, we'll listen to both content's resize event and browser's load end;
// since a racing condition can happen, where the content's listener is added after
// the resize, because the content's document was reloaded; therefore the test would
// hang forever. See bug 1302879.
let browser = ui.getViewportBrowser();
let onResize = (_, data) => { let onResize = (_, data) => {
if (data.width != width || data.height != height) { if (!isSizeMatching(data)) {
return; return;
} }
ui.off("content-resize", onResize); ui.off("content-resize", onResize);
browser.removeEventListener("mozbrowserloadend", onBrowserLoadEnd);
info(`Got content-resize to ${width} x ${height}`); info(`Got content-resize to ${width} x ${height}`);
resolve(); resolve();
}; };
let onBrowserLoadEnd = Task.async(function* () {
let data = yield getContentSize(ui);
onResize(undefined, data);
});
info(`Waiting for content-resize to ${width} x ${height}`); info(`Waiting for content-resize to ${width} x ${height}`);
ui.on("content-resize", onResize); ui.on("content-resize", onResize);
}); browser.addEventListener("mozbrowserloadend",
onBrowserLoadEnd, { once: true });
}));
} }
var setViewportSize = Task.async(function* (ui, manager, width, height) { var setViewportSize = Task.async(function* (ui, manager, width, height) {
@ -250,6 +274,13 @@ function getSessionHistory(browser) {
}); });
} }
function getContentSize(ui) {
return spawnViewportTask(ui, {}, () => ({
width: content.screen.width,
height: content.screen.height
}));
}
function waitForPageShow(browser) { function waitForPageShow(browser) {
let mm = browser.messageManager; let mm = browser.messageManager;
return new Promise(resolve => { return new Promise(resolve => {

View File

@ -5,7 +5,7 @@
#sidebar-panel-computedview { #sidebar-panel-computedview {
margin: 0; margin: 0;
display : flex; display: flex;
flex-direction: column; flex-direction: column;
width: 100%; width: 100%;
height: 100%; height: 100%;
@ -61,7 +61,8 @@
} }
.property-view { .property-view {
padding: 2px 17px; padding: 2px 0px;
padding-inline-start: 5px;
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
} }
@ -105,17 +106,21 @@
overflow-x: hidden; overflow-x: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
padding-left: 10px; padding-inline-start: 14px;
outline: 0 !important; outline: 0 !important;
} }
.other-property-value { .other-property-value {
background-position: left center; background-position: left center;
padding-left: 8px; padding-inline-start: 8px;
}
.other-property-value:dir(rtl) {
background-position-x: right;
} }
.property-content { .property-content {
padding-left: 17px; padding-inline-start: 17px;
} }
.theme-firebug .property-view, .theme-firebug .property-view,
@ -130,7 +135,6 @@
/* From skin */ /* From skin */
.expander { .expander {
visibility: hidden; visibility: hidden;
margin-inline-start: -12px !important;
} }
.expandable { .expandable {
@ -143,7 +147,8 @@
.matchedselectors > p { .matchedselectors > p {
clear: both; clear: both;
margin: 0 2px 0 0; margin: 0;
margin-inline-end: 2px;
padding: 2px; padding: 2px;
overflow-x: hidden; overflow-x: hidden;
border-style: dotted; border-style: dotted;
@ -181,6 +186,11 @@
float: right; float: right;
} }
/* Workaround until float: inline-end; is enabled by default */
.link:dir(rtl) {
float: left;
}
/* Take away these two :visited rules to get a core dumper */ /* Take away these two :visited rules to get a core dumper */
/* See https://bugzilla.mozilla.org/show_bug.cgi?id=575675#c30 */ /* See https://bugzilla.mozilla.org/show_bug.cgi?id=575675#c30 */

View File

@ -51,7 +51,8 @@ window {
/* Minimum width for the Inspector main (uncontrolled) area. */ /* Minimum width for the Inspector main (uncontrolled) area. */
#inspector-splitter-box .uncontrolled { #inspector-splitter-box .uncontrolled {
min-width: 275px; min-height: 50px;
min-width: 50px;
} }
#inspector-splitter-box .controlled.pane-collapsed { #inspector-splitter-box .controlled.pane-collapsed {

View File

@ -44,6 +44,7 @@ Cu.import("resource://services-sync/engines.js");
Cu.import("resource://services-sync/record.js"); Cu.import("resource://services-sync/record.js");
Cu.import("resource://services-sync/util.js"); Cu.import("resource://services-sync/util.js");
Cu.import("resource://services-sync/constants.js"); Cu.import("resource://services-sync/constants.js");
Cu.import("resource://services-sync/collection_validator.js");
Cu.import("resource://services-common/async.js"); Cu.import("resource://services-common/async.js");
Cu.import("resource://gre/modules/Preferences.jsm"); Cu.import("resource://gre/modules/Preferences.jsm");
@ -53,7 +54,7 @@ XPCOMUtils.defineLazyModuleGetter(this, "AddonManager",
XPCOMUtils.defineLazyModuleGetter(this, "AddonRepository", XPCOMUtils.defineLazyModuleGetter(this, "AddonRepository",
"resource://gre/modules/addons/AddonRepository.jsm"); "resource://gre/modules/addons/AddonRepository.jsm");
this.EXPORTED_SYMBOLS = ["AddonsEngine"]; this.EXPORTED_SYMBOLS = ["AddonsEngine", "AddonValidator"];
// 7 days in milliseconds. // 7 days in milliseconds.
const PRUNE_ADDON_CHANGES_THRESHOLD = 60 * 60 * 24 * 7 * 1000; const PRUNE_ADDON_CHANGES_THRESHOLD = 60 * 60 * 24 * 7 * 1000;
@ -176,7 +177,7 @@ AddonsEngine.prototype = {
continue; continue;
} }
if (!this._store.isAddonSyncable(addons[id])) { if (!this.isAddonSyncable(addons[id])) {
continue; continue;
} }
@ -234,6 +235,10 @@ AddonsEngine.prototype = {
let cb = Async.makeSpinningCallback(); let cb = Async.makeSpinningCallback();
this._reconciler.refreshGlobalState(cb); this._reconciler.refreshGlobalState(cb);
cb.wait(); cb.wait();
},
isAddonSyncable(addon, ignoreRepoCheck) {
return this._store.isAddonSyncable(addon, ignoreRepoCheck);
} }
}; };
@ -533,9 +538,12 @@ AddonsStore.prototype = {
* *
* @param addon * @param addon
* Addon instance * Addon instance
* @param ignoreRepoCheck
* Should we skip checking the Addons repository (primarially useful
* for testing and validation).
* @return Boolean indicating whether it is appropriate for Sync * @return Boolean indicating whether it is appropriate for Sync
*/ */
isAddonSyncable: function isAddonSyncable(addon) { isAddonSyncable: function isAddonSyncable(addon, ignoreRepoCheck = false) {
// Currently, we limit syncable add-ons to those that are: // Currently, we limit syncable add-ons to those that are:
// 1) In a well-defined set of types // 1) In a well-defined set of types
// 2) Installed in the current profile // 2) Installed in the current profile
@ -592,8 +600,9 @@ AddonsStore.prototype = {
// If the AddonRepository's cache isn't enabled (which it typically isn't // If the AddonRepository's cache isn't enabled (which it typically isn't
// in tests), getCachedAddonByID always returns null - so skip the check // in tests), getCachedAddonByID always returns null - so skip the check
// in that case. // in that case. We also provide a way to specifically opt-out of the check
if (!AddonRepository.cacheEnabled) { // even if the cache is enabled, which is used by the validators.
if (ignoreRepoCheck || !AddonRepository.cacheEnabled) {
return true; return true;
} }
@ -736,3 +745,69 @@ AddonsTracker.prototype = {
this.reconciler.stopListening(); this.reconciler.stopListening();
}, },
}; };
class AddonValidator extends CollectionValidator {
constructor(engine = null) {
super("addons", "id", [
"addonID",
"enabled",
"applicationID",
"source"
]);
this.engine = engine;
}
getClientItems() {
return Promise.all([
new Promise(resolve =>
AddonManager.getAllAddons(resolve)),
new Promise(resolve =>
AddonManager.getAddonsWithOperationsByTypes(["extension", "theme"], resolve)),
]).then(([installed, addonsWithPendingOperation]) => {
// Addons pending install won't be in the first list, but addons pending
// uninstall/enable/disable will be in both lists.
let all = new Map(installed.map(addon => [addon.id, addon]));
for (let addon of addonsWithPendingOperation) {
all.set(addon.id, addon);
}
// Convert to an array since Map.prototype.values returns an iterable
return [...all.values()];
});
}
normalizeClientItem(item) {
let enabled = !item.userDisabled;
if (item.pendingOperations & AddonManager.PENDING_ENABLE) {
enabled = true;
} else if (item.pendingOperations & AddonManager.PENDING_DISABLE) {
enabled = false;
}
return {
enabled,
id: item.syncGUID,
addonID: item.id,
applicationID: Services.appinfo.ID,
source: "amo", // check item.foreignInstall?
original: item
};
}
normalizeServerItem(item) {
let guid = this.engine._findDupe(item);
if (guid) {
item.id = guid;
}
return item;
}
clientUnderstands(item) {
return item.applicationID === Services.appinfo.ID;
}
syncedByClient(item) {
return !item.original.hidden &&
!item.original.isSystem &&
!(item.original.pendingOperations & AddonManager.PENDING_UNINSTALL) &&
this.engine.isAddonSyncable(item.original, true);
}
}

View File

@ -33,7 +33,8 @@ Phase("phase01", [
[Sync] [Sync]
]); ]);
Phase("phase02", [ Phase("phase02", [
[Addons.verify, [id], STATE_ENABLED] [Addons.verify, [id], STATE_ENABLED],
[Sync]
]); ]);
Phase("phase03", [ Phase("phase03", [
[Addons.verifyNot, [id]], [Addons.verifyNot, [id]],
@ -41,6 +42,7 @@ Phase("phase03", [
]); ]);
Phase("phase04", [ Phase("phase04", [
[Addons.verify, [id], STATE_ENABLED], [Addons.verify, [id], STATE_ENABLED],
[Sync]
]); ]);
// Now we disable the add-on // Now we disable the add-on
@ -51,13 +53,15 @@ Phase("phase05", [
]); ]);
Phase("phase06", [ Phase("phase06", [
[Addons.verify, [id], STATE_DISABLED], [Addons.verify, [id], STATE_DISABLED],
[Sync]
]); ]);
Phase("phase07", [ Phase("phase07", [
[Addons.verify, [id], STATE_ENABLED], [Addons.verify, [id], STATE_ENABLED],
[Sync] [Sync]
]); ]);
Phase("phase08", [ Phase("phase08", [
[Addons.verify, [id], STATE_DISABLED] [Addons.verify, [id], STATE_DISABLED],
[Sync]
]); ]);
// Now we re-enable it again. // Now we re-enable it again.
@ -68,13 +72,15 @@ Phase("phase09", [
]); ]);
Phase("phase10", [ Phase("phase10", [
[Addons.verify, [id], STATE_ENABLED], [Addons.verify, [id], STATE_ENABLED],
[Sync]
]); ]);
Phase("phase11", [ Phase("phase11", [
[Addons.verify, [id], STATE_DISABLED], [Addons.verify, [id], STATE_DISABLED],
[Sync] [Sync]
]); ]);
Phase("phase12", [ Phase("phase12", [
[Addons.verify, [id], STATE_ENABLED] [Addons.verify, [id], STATE_ENABLED],
[Sync]
]); ]);
// And we uninstall it // And we uninstall it
@ -86,12 +92,14 @@ Phase("phase13", [
[Sync] [Sync]
]); ]);
Phase("phase14", [ Phase("phase14", [
[Addons.verifyNot, [id]] [Addons.verifyNot, [id]],
[Sync]
]); ]);
Phase("phase15", [ Phase("phase15", [
[Addons.verify, [id], STATE_ENABLED], [Addons.verify, [id], STATE_ENABLED],
[Sync] [Sync]
]); ]);
Phase("phase16", [ Phase("phase16", [
[Addons.verifyNot, [id]] [Addons.verifyNot, [id]],
[Sync]
]); ]);

View File

@ -34,6 +34,9 @@ Phase("phase02", [
Phase("phase03", [ Phase("phase03", [
[Sync], // Get GUID updates, potentially. [Sync], // Get GUID updates, potentially.
[Addons.setEnabled, [id], STATE_DISABLED], [Addons.setEnabled, [id], STATE_DISABLED],
// We've changed the state, but don't want this profile to sync until phase5,
// so if we ran a validation now we'd be expecting to find errors.
[Addons.skipValidation]
]); ]);
Phase("phase04", [ Phase("phase04", [
[EnsureTracking], [EnsureTracking],

View File

@ -25,5 +25,6 @@ Phase("phase1", [
Phase("phase2", [ Phase("phase2", [
// Add-on should be present after restart // Add-on should be present after restart
[Addons.verify, [id], STATE_ENABLED] [Addons.verify, [id], STATE_ENABLED],
[Sync] // Sync to ensure everything is initialized enough for the addon validator to run
]); ]);

View File

@ -30,5 +30,6 @@ Phase("phase02", [
]); ]);
Phase("phase03", [ Phase("phase03", [
[Addons.verify, [id1], STATE_ENABLED], [Addons.verify, [id1], STATE_ENABLED],
[Addons.verify, [id2], STATE_ENABLED] [Addons.verify, [id2], STATE_ENABLED],
[Sync] // Sync to ensure that the addon validator can run without error
]); ]);

View File

@ -26,6 +26,7 @@ Cu.import("resource://services-sync/util.js");
Cu.import("resource://services-sync/bookmark_validator.js"); Cu.import("resource://services-sync/bookmark_validator.js");
Cu.import("resource://services-sync/engines/passwords.js"); Cu.import("resource://services-sync/engines/passwords.js");
Cu.import("resource://services-sync/engines/forms.js"); Cu.import("resource://services-sync/engines/forms.js");
Cu.import("resource://services-sync/engines/addons.js");
// TPS modules // TPS modules
Cu.import("resource://tps/logger.jsm"); Cu.import("resource://tps/logger.jsm");
@ -113,6 +114,7 @@ var TPS = {
_triggeredSync: false, _triggeredSync: false,
_usSinceEpoch: 0, _usSinceEpoch: 0,
_requestedQuit: false, _requestedQuit: false,
shouldValidateAddons: false,
shouldValidateBookmarks: false, shouldValidateBookmarks: false,
shouldValidatePasswords: false, shouldValidatePasswords: false,
shouldValidateForms: false, shouldValidateForms: false,
@ -464,6 +466,7 @@ var TPS = {
}, },
HandleAddons: function (addons, action, state) { HandleAddons: function (addons, action, state) {
this.shouldValidateAddons = true;
for (let entry of addons) { for (let entry of addons) {
Logger.logInfo("executing action " + action.toUpperCase() + Logger.logInfo("executing action " + action.toUpperCase() +
" on addon " + JSON.stringify(entry)); " on addon " + JSON.stringify(entry));
@ -661,66 +664,64 @@ var TPS = {
Logger.logInfo("Bookmark validation finished"); Logger.logInfo("Bookmark validation finished");
}, },
ValidatePasswords() { ValidateCollection(engineName, ValidatorType) {
let serverRecordDumpStr;
try {
Logger.logInfo("About to perform password validation");
let pwEngine = Weave.Service.engineManager.get("passwords");
let validator = new PasswordValidator();
let serverRecords = validator.getServerItems(pwEngine);
let clientRecords = Async.promiseSpinningly(validator.getClientItems());
serverRecordDumpStr = JSON.stringify(serverRecords);
let { problemData } = validator.compareClientWithServer(clientRecords, serverRecords);
for (let { name, count } of problemData.getSummary()) {
if (count) {
Logger.logInfo(`Validation problem: "${name}": ${JSON.stringify(problemData[name])}`);
}
Logger.AssertEqual(count, 0, `Password validation error of type ${name}`);
}
} catch (e) {
// Dump the client records (should always be doable)
DumpPasswords();
// Dump the server records if gotten them already.
if (serverRecordDumpStr) {
Logger.logInfo("Server password records:\n" + serverRecordDumpStr + "\n");
}
this.DumpError("Password validation failed", e);
}
Logger.logInfo("Password validation finished");
},
ValidateForms() {
let serverRecordDumpStr; let serverRecordDumpStr;
let clientRecordDumpStr; let clientRecordDumpStr;
try { try {
Logger.logInfo("About to perform form validation"); Logger.logInfo(`About to perform validation for "${engineName}"`);
let engine = Weave.Service.engineManager.get("forms"); let engine = Weave.Service.engineManager.get(engineName);
let validator = new FormValidator(); let validator = new ValidatorType(engine);
let serverRecords = validator.getServerItems(engine); let serverRecords = validator.getServerItems(engine);
let clientRecords = Async.promiseSpinningly(validator.getClientItems()); let clientRecords = Async.promiseSpinningly(validator.getClientItems());
clientRecordDumpStr = JSON.stringify(clientRecords, undefined, 2); try {
serverRecordDumpStr = JSON.stringify(serverRecords, undefined, 2); // This substantially improves the logs for addons while not making a
// substantial difference for the other two
clientRecordDumpStr = JSON.stringify(clientRecords.map(r => {
let res = validator.normalizeClientItem(r);
delete res.original; // Try and prevent cyclic references
return res;
}));
} catch (e) {
// ignore the error, the dump string is just here to make debugging easier.
clientRecordDumpStr = "<Cyclic value>";
}
try {
serverRecordDumpStr = JSON.stringify(serverRecords);
} catch (e) {
// as above
serverRecordDumpStr = "<Cyclic value>";
}
let { problemData } = validator.compareClientWithServer(clientRecords, serverRecords); let { problemData } = validator.compareClientWithServer(clientRecords, serverRecords);
for (let { name, count } of problemData.getSummary()) { for (let { name, count } of problemData.getSummary()) {
if (count) { if (count) {
Logger.logInfo(`Validation problem: "${name}": ${JSON.stringify(problemData[name])}`); Logger.logInfo(`Validation problem: "${name}": ${JSON.stringify(problemData[name])}`);
} }
Logger.AssertEqual(count, 0, `Form validation error of type ${name}`); Logger.AssertEqual(count, 0, `Validation error for "${engineName}" of type "${name}"`);
} }
} catch (e) { } catch (e) {
// Dump the client records if possible // Dump the client records if possible
if (clientRecordDumpStr) { if (clientRecordDumpStr) {
Logger.logInfo("Client forms records:\n" + clientRecordDumpStr + "\n"); Logger.logInfo(`Client state for ${engineName}:\n${clientRecordDumpStr}\n`);
} }
// Dump the server records if gotten them already. // Dump the server records if gotten them already.
if (serverRecordDumpStr) { if (serverRecordDumpStr) {
Logger.logInfo("Server forms records:\n" + serverRecordDumpStr + "\n"); Logger.logInfo(`Server state for ${engineName}:\n${serverRecordDumpStr}\n`);
} }
this.DumpError("Form validation failed", e); this.DumpError(`Validation failed for ${engineName}`, e);
} }
Logger.logInfo("Form validation finished"); Logger.logInfo(`Validation finished for ${engineName}`);
},
ValidatePasswords() {
return this.ValidateCollection("passwords", PasswordValidator);
},
ValidateForms() {
return this.ValidateCollection("forms", FormValidator);
},
ValidateAddons() {
return this.ValidateCollection("addons", AddonValidator);
}, },
RunNextTestAction: function() { RunNextTestAction: function() {
@ -737,6 +738,9 @@ var TPS = {
if (this.shouldValidateForms) { if (this.shouldValidateForms) {
this.ValidateForms(); this.ValidateForms();
} }
if (this.shouldValidateAddons) {
this.ValidateAddons();
}
// we're all done // we're all done
Logger.logInfo("test phase " + this._currentPhase + ": " + Logger.logInfo("test phase " + this._currentPhase + ": " +
(this._errors ? "FAIL" : "PASS")); (this._errors ? "FAIL" : "PASS"));
@ -1137,6 +1141,9 @@ var Addons = {
verifyNot: function Addons__verifyNot(addons) { verifyNot: function Addons__verifyNot(addons) {
TPS.HandleAddons(addons, ACTION_VERIFY_NOT); TPS.HandleAddons(addons, ACTION_VERIFY_NOT);
}, },
skipValidation() {
TPS.shouldValidateAddons = false;
}
}; };
var Bookmarks = { var Bookmarks = {