Merge autoland to mozilla-central r=merge a=merge

This commit is contained in:
btara 2017-11-14 22:31:54 +02:00
Родитель 7af6788dd0 776dcceab4
Коммит 9607440bf8
96 изменённых файлов: 851 добавлений и 1845 удалений

Просмотреть файл

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="8 8 16 16"><title>Icons / Extension</title><g fill="none"><path d="M0 0h16v16H0z"/><path d="M22.5 16c-1 0-1 1-1.7 1-.5 0-.8-.3-.8-.7V13c0-.6-.4-1-1-1h-3.2c-.5 0-.8-.3-.8-.7 0-.8 1-.8 1-1.8 0-.9-.9-1.5-2-1.5s-2 .6-2 1.5c0 1 1 1 1 1.8 0 .4-.3.7-.7.7H9c-.6 0-1 .4-1 1v2.3c0 .4.3.7.8.7.7 0 .7-1 1.7-1 .9 0 1.5.9 1.5 2s-.6 2-1.5 2c-1 0-1-1-1.7-1-.5 0-.8.3-.8.8V23c0 .6.4 1 1 1h3.3c.4 0 .7-.3.7-.7 0-.8-1-.8-1-1.8 0-.9.9-1.5 2-1.5s2 .6 2 1.5c0 1-1 1-1 1.8 0 .4.3.7.8.7H19c.6 0 1-.4 1-1v-3.2c0-.5.3-.8.8-.8.7 0 .7 1 1.7 1 .9 0 1.5-.9 1.5-2s-.6-2-1.5-2z" fill="#0A84FF"/></g></svg>

До

Ширина:  |  Высота:  |  Размер: 645 B

Просмотреть файл

@ -1 +0,0 @@
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><title>Glyph / Customize</title><g id="Symbols" fill="none" fill-rule="evenodd"><g id="Glyph-/-Customize" fill-rule="nonzero" fill="#0A84FF"><path d="M4 10c-.886.002-1.665.59-1.91 1.44 0 .01-.015.015-.018.025-.362 1.135-.705 2.11-1.76 2.573l-.022.012-.024.012c-.162.086-.265.254-.266.438 0 .276.224.5.5.5 1.74.12 3.46-.414 4.825-1.5.006-.006.007-.013.013-.02.62-.55.832-1.428.534-2.202C5.575 10.504 4.83 9.995 4 10zM15.693.307c-.365-.363-.95-.383-1.338-.046l-8.03 7c-.206.18-.327.435-.336.707-.01.27.093.535.285.727l1.032 1.03c.184.185.433.288.693.288h.033c.272-.01.527-.13.706-.335l7-8.03c.338-.39.318-.975-.047-1.34z" id="Shape"/></g></g></svg>

До

Ширина:  |  Высота:  |  Размер: 729 B

Просмотреть файл

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><title>default-browser-16</title><path fill="#0A84FF" d="M8,6s0-4,3.5-4S15,5,15,6c0,4.5-7,9-7,9Z"/><path fill="#0A84FF" d="M8,6S8,2,4.5,2,1,5,1,6c0,4.5,7,9,7,9L9,9Z"/></svg>

До

Ширина:  |  Высота:  |  Размер: 257 B

Просмотреть файл

@ -1 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?><svg width="92px" height="92px" viewBox="0 0 92 92" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><title>Tip / Icon / Library</title><desc>Created with Sketch.</desc><defs></defs><g id="Symbols" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"><g id="Tip-/-Icon-/-Library" fill-rule="nonzero" fill="#0C0C0D"><g id="Icon-/-Library-/-Web"><path fill="#0A84FF" d="M28.7405828,17.2350375 C25.5662458,17.2350375 22.9929371,19.8060508 22.9929371,22.9775563 L22.9929371,80.402744 C22.9929371,83.5742496 25.5662458,86.1452628 28.7405828,86.1452628 C31.9149199,86.1452628 34.4882285,83.5742496 34.4882285,80.402744 L34.4882285,22.9775563 C34.4882285,19.8060508 31.9149199,17.2350375 28.7405828,17.2350375 Z M45.98352,11.4925188 C42.8091829,11.4925188 40.2358743,14.063532 40.2358743,17.2350375 L40.2358743,80.402744 C40.2358743,83.5742496 42.8091829,86.1452628 45.98352,86.1452628 C49.157857,86.1452628 51.7311657,83.5742496 51.7311657,80.402744 L51.7311657,17.2350375 C51.7311657,14.063532 49.157857,11.4925188 45.98352,11.4925188 Z M91.6140792,78.4388026 L68.6234964,15.2710961 C67.9500245,13.3049026 66.2658683,11.8556604 64.2198302,11.4816739 C62.1737921,11.1076875 60.0851643,11.8673187 58.7585671,13.4679283 C57.4319699,15.0685378 57.0744241,17.2603443 57.8236701,19.198979 L80.814253,82.3666855 C81.4877249,84.332879 83.1718811,85.7821212 85.2179192,86.1561076 C87.2639573,86.5300941 89.3525851,85.7704629 90.6791823,84.1698533 C92.0057794,82.5692438 92.3633253,80.3774372 91.6140792,78.4388026 L91.6140792,78.4388026 Z M11.4976457,5.75 C8.32330864,5.75 5.75,8.32101323 5.75,11.4925188 L5.75,80.402744 C5.75,83.5742496 8.32330864,86.1452628 11.4976457,86.1452628 C14.6719828,86.1452628 17.2452914,83.5742496 17.2452914,80.402744 L17.2452914,11.4925188 C17.2452914,8.32101323 14.6719828,5.75 11.4976457,5.75 Z" id="Shape"></path></g></g></g></svg>

До

Ширина:  |  Высота:  |  Размер: 1.9 KiB

Просмотреть файл

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path fill="#0A84FF" d="M8 1a8.009 8.009 0 0 0-8 8 7.917 7.917 0 0 0 .78 3.43 1 1 0 1 0 1.8-.86A5.943 5.943 0 0 1 2 9a6 6 0 1 1 11.414 2.571 1 1 0 1 0 1.807.858A7.988 7.988 0 0 0 8 1z"/><path fill="#0A84FF" d="M11.769 7.078a.5.5 0 0 0-.69.153L8.616 11.1a2 2 0 1 0 .5 3.558 2.011 2.011 0 0 0 .54-.54 1.954 1.954 0 0 0-.2-2.479l2.463-3.871a.5.5 0 0 0-.15-.69z"/></svg>

До

Ширина:  |  Высота:  |  Размер: 450 B

Просмотреть файл

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="8 8 16 16"><title>Icons / Private Browsing</title><g fill="none"><path d="M0 0h32v32H0z"/><path d="M20.4 20c-1.7 0-2.8-2-4.4-2-1.6 0-2.8 2-4.4 2-2 0-3.5-2-3.5-5.3-.1-2 .6-2.7 3.2-2.7s3.4 1.1 4.7 1.1c1.3 0 2.1-1.1 4.7-1.1s3.3.7 3.2 2.7c0 3.3-1.5 5.3-3.5 5.3zm-7.8-5.4c-1.6 0-2.3 1-2.3 1.2 0 .3 1.1.9 2.1.9 1.1 0 2.3-.4 2.3-.7-.2-1-1.1-1.6-2.1-1.4zm6.8 0c-1-.2-1.9.4-2.1 1.4 0 .3 1.2.7 2.3.7 1 0 2.1-.6 2.1-.9 0-.2-.7-1.2-2.3-1.2z" fill="#0A84FF"/></g></svg>

До

Ширина:  |  Высота:  |  Размер: 528 B

Просмотреть файл

@ -1 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?><svg width="92px" height="92px" viewBox="0 0 92 92" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><title>Tip / Icon / Screenshots</title><desc>Created with Sketch.</desc><defs></defs><g id="Symbols" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"><g id="Tip-/-Icon-/-Screenshots" fill-rule="nonzero" fill="#0C0C0D"><g id="Icon-/-Screenshot-/-Web"><path fill="#0A84FF" d="M23.0526905,5.75 C16.7062659,5.75 11.5614723,10.8982866 11.5614723,17.2490201 L23.0526905,17.2490201 L23.0526905,5.75 Z M57.5263453,5.75 L46.035127,5.75 L46.035127,17.2490201 L57.5263453,17.2490201 L57.5263453,5.75 Z M80.5087818,5.75 L80.5087818,17.2490201 L92,17.2490201 C92,10.8982866 86.8552063,5.75 80.5087818,5.75 Z M40.2895179,5.75 L28.7982997,5.75 L28.7982997,17.2490201 L40.2895179,17.2490201 L40.2895179,5.75 Z M74.7631726,5.75 L63.2719544,5.75 L63.2719544,17.2490201 L74.7631726,17.2490201 L74.7631726,5.75 Z M80.5087818,34.4975502 L92,34.4975502 L92,22.9985301 L80.5087818,22.9985301 L80.5087818,34.4975502 Z M80.5087818,68.9946104 C86.8552063,68.9946104 92,63.8463237 92,57.4955903 L80.5087818,57.4955903 L80.5087818,68.9946104 Z M80.5087818,51.7460803 L92,51.7460803 L92,40.2470602 L80.5087818,40.2470602 L80.5087818,51.7460803 Z M77.9749681,39.286892 C74.3364854,34.0846734 67.1729138,32.8182928 61.9734467,36.4581331 L39.9390357,52.734996 L28.631677,44.8006721 C28.7205927,44.2448747 28.7762328,43.6842562 28.7982997,43.1218152 C28.7892628,38.6172543 26.6604054,34.3800822 23.0526905,31.6860398 L23.0526905,22.9985301 L11.5614723,22.9985301 L11.5614723,29.0355156 C5.79583786,30.1835386 1.31120668,34.7313256 0.240775953,40.5156383 C-0.829654779,46.2999509 1.73019662,52.1531434 6.70268981,55.2910372 C11.675183,58.4289309 18.0565494,58.2180974 22.811375,54.7588235 L29.9474215,59.7551477 L21.9035687,65.4011666 C16.3310302,62.0167647 9.17796578,62.8225007 4.49677041,67.3619045 C-0.184424965,71.9013083 -1.21401,79.0303725 1.99130168,84.710299 C5.19661336,90.3902255 11.8290284,93.1895755 18.1311131,91.5224455 C24.4331979,89.8553154 28.8167193,84.1418505 28.7982997,77.6188754 C28.7959008,76.6687877 28.699673,75.721263 28.5110192,74.7901165 L77.9749681,39.286892 Z M14.4342769,50.3087028 C10.4677615,50.3087028 7.25226545,47.0910236 7.25226545,43.1218152 C7.25226545,39.1526068 10.4677615,35.9349277 14.4342769,35.9349277 C18.4007922,35.9349277 21.6162883,39.1526068 21.6162883,43.1218152 C21.6162883,47.0910236 18.4007922,50.3087028 14.4342769,50.3087028 Z M14.4342769,84.805763 C10.4677615,84.805763 7.25226545,81.5880838 7.25226545,77.6188754 C7.25226545,73.649667 10.4677615,70.4319879 14.4342769,70.4319879 C18.4007922,70.4319879 21.6162883,73.649667 21.6162883,77.6188754 C21.6162883,81.5880838 18.4007922,84.805763 14.4342769,84.805763 Z M45.4605661,70.8402031 L62.7950688,84.0640762 C67.9945359,87.7039165 75.1581075,86.4375358 78.7965902,81.2353172 L55.457926,63.8200513 L45.4605661,70.8402031 Z" id="Shape"></path></g></g></g></svg>

До

Ширина:  |  Высота:  |  Размер: 3.0 KiB

Просмотреть файл

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="8 8 16 16 "><title>Icons / Search</title><g fill="none"><path d="M0 0h32v32H0z"/><path d="M23.7 22.3l-4.8-4.8c1.8-2.5 1.4-6.1-1-8.1s-5.9-1.9-8.1.4c-2.3 2.2-2.4 5.7-.4 8.1 2 2.4 5.6 2.8 8.1 1l4.8 4.8c.4.4 1 .4 1.4 0 .4-.4.4-1 0-1.4zM14 18c-2.2 0-4-1.8-4-4s1.8-4 4-4 4 1.8 4 4c0 1.1-.4 2.1-1.1 2.9-.8.7-1.8 1.1-2.9 1.1z" fill="#0A84FF"/></g></svg>

До

Ширина:  |  Высота:  |  Размер: 417 B

Просмотреть файл

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="8 8 16 16"><title> Icons &#x2F; Sync</title><desc> Created with Sketch.</desc><g fill="none"><rect width="32" height="32"/><path d="M22 9C21.4 9 21 9.4 21 10L21 11.1C19.2 9.3 16.6 8.6 14.2 9.2 11.7 9.9 9.8 11.8 9.2 14.3 9.1 14.7 9.2 15 9.5 15.3 9.8 15.5 10.1 15.6 10.5 15.5 10.8 15.4 11.1 15.1 11.2 14.8 11.7 12.6 13.7 11 16 11 17.6 11 19 11.7 20 13L18 13C17.4 13 17 13.4 17 14 17 14.6 17.4 15 18 15L22 15C22.6 15 23 14.6 23 14L23 10C23 9.4 22.6 9 22 9ZM22 16.5C21.8 16.4 21.5 16.5 21.3 16.6 21.1 16.7 20.9 17 20.8 17.2 20.3 19.4 18.3 21 16 21 14.4 21 13 20.3 12 19L14 19C14.6 19 15 18.6 15 18 15 17.4 14.6 17 14 17L10 17C9.4 17 9 17.4 9 18L9 22C9 22.6 9.4 23 10 23 10.6 23 11 22.6 11 22L11 20.9C12.8 22.7 15.4 23.4 17.8 22.8 20.3 22.1 22.2 20.2 22.8 17.7 22.9 17.2 22.6 16.6 22 16.5Z" fill="#0A84FF"/></g></svg>

До

Ширина:  |  Высота:  |  Размер: 887 B

Просмотреть файл

@ -200,6 +200,7 @@
#onboarding-tour-list .onboarding-tour-item-container {
list-style: none;
outline: none;
position: relative;
}
#onboarding-tour-list .onboarding-tour-item {
@ -209,12 +210,10 @@
padding-top: 14px;
padding-bottom: 14px;
margin-bottom: 9px;
background-repeat: no-repeat;
background-position: left 17px top 14px;
background-size: 20px;
font-size: 16px;
cursor: pointer;
max-height: 54px;
--onboarding-tour-item-active-color: #0A84FF;
}
#onboarding-tour-list .onboarding-tour-item:dir(rtl) {
@ -233,9 +232,32 @@
padding-inline-start: 29px;
}
#onboarding-tour-list .onboarding-tour-item::after {
content: "";
display: block;
width: 48px;
height: 48px;
position: absolute;
offset-inline-start: 0px;
top: 0px;
background-color: #3E3D40;
mask-repeat: no-repeat;
mask-position: left 17px top 14px;
mask-size: 20px;
}
#onboarding-tour-list .onboarding-tour-item:dir(rtl)::after {
mask-position: right 17px top 14px;
}
#onboarding-tour-list .onboarding-tour-item.onboarding-active::after,
#onboarding-tour-list .onboarding-tour-item-container:hover .onboarding-tour-item::after {
background-color: var(--onboarding-tour-item-active-color);
}
#onboarding-tour-list .onboarding-tour-item.onboarding-active,
#onboarding-tour-list .onboarding-tour-item-container:hover .onboarding-tour-item {
color: #0A84FF;
color: var(--onboarding-tour-item-active-color);
/* With 1px transparent outline, could see a border in the high-constrast mode */
outline: 1px solid transparent;
}
@ -396,94 +418,49 @@
}
/* Tour Icons */
#onboarding-tour-singlesearch,
#onboarding-tour-singlesearch.onboarding-tour-item::after,
#onboarding-notification-bar[data-target-tour-id=onboarding-tour-singlesearch] #onboarding-notification-tour-title::before {
background-image: url("img/icons_singlesearch.svg");
mask-image: url("img/icons_singlesearch.svg");
}
#onboarding-tour-singlesearch.onboarding-active,
.onboarding-tour-item-container:hover #onboarding-tour-singlesearch {
background-image: url("img/icons_singlesearch-colored.svg");
}
#onboarding-tour-private-browsing,
#onboarding-tour-private-browsing.onboarding-tour-item::after,
#onboarding-notification-bar[data-target-tour-id=onboarding-tour-private-browsing] #onboarding-notification-tour-title::before {
background-image: url("img/icons_private.svg");
mask-image: url("img/icons_private.svg");
}
#onboarding-tour-private-browsing.onboarding-active,
.onboarding-tour-item-container:hover #onboarding-tour-private-browsing {
background-image: url("img/icons_private-colored.svg");
}
#onboarding-tour-addons,
#onboarding-tour-addons.onboarding-tour-item::after,
#onboarding-notification-bar[data-target-tour-id=onboarding-tour-addons] #onboarding-notification-tour-title::before {
background-image: url("img/icons_addons.svg");
mask-image: url("img/icons_addons.svg");
}
#onboarding-tour-addons.onboarding-active,
.onboarding-tour-item-container:hover #onboarding-tour-addons {
background-image: url("img/icons_addons-colored.svg");
}
#onboarding-tour-customize,
#onboarding-tour-customize.onboarding-tour-item::after,
#onboarding-notification-bar[data-target-tour-id=onboarding-tour-customize] #onboarding-notification-tour-title::before {
background-image: url("img/icons_customize.svg");
mask-image: url("img/icons_customize.svg");
}
#onboarding-tour-customize.onboarding-active,
.onboarding-tour-item-container:hover #onboarding-tour-customize {
background-image: url("img/icons_customize-colored.svg");
}
#onboarding-tour-default-browser ,
#onboarding-tour-default-browser.onboarding-tour-item::after,
#onboarding-notification-bar[data-target-tour-id=onboarding-tour-default-browser] #onboarding-notification-tour-title::before {
background-image: url("img/icons_default.svg");
mask-image: url("img/icons_default.svg");
}
#onboarding-tour-default-browser.onboarding-active,
.onboarding-tour-item-container:hover #onboarding-tour-default-browser {
background-image: url("img/icons_default-colored.svg");
}
#onboarding-tour-sync,
#onboarding-tour-sync.onboarding-tour-item::after,
#onboarding-notification-bar[data-target-tour-id=onboarding-tour-sync] #onboarding-notification-tour-title::before {
background-image: url("img/icons_sync.svg");
mask-image: url("img/icons_sync.svg");
}
#onboarding-tour-sync.onboarding-active,
.onboarding-tour-item-container:hover #onboarding-tour-sync {
background-image: url("img/icons_sync-colored.svg");
}
#onboarding-tour-library,
#onboarding-tour-library.onboarding-tour-item::after,
#onboarding-notification-bar[data-target-tour-id=onboarding-tour-library] #onboarding-notification-tour-title::before {
background-image: url("img/icons_library.svg");
mask-image: url("img/icons_library.svg");
}
#onboarding-tour-library.onboarding-active,
.onboarding-tour-item-container:hover #onboarding-tour-library {
background-image: url("img/icons_library-colored.svg");
}
#onboarding-tour-performance,
#onboarding-tour-performance.onboarding-tour-item::after,
#onboarding-notification-bar[data-target-tour-id=onboarding-tour-performance] #onboarding-notification-tour-title::before {
background-image: url("img/icons_performance.svg");
mask-image: url("img/icons_performance.svg");
}
#onboarding-tour-performance.onboarding-active,
.onboarding-tour-item-container:hover #onboarding-tour-performance {
background-image: url("img/icons_performance-colored.svg");
}
#onboarding-tour-screenshots,
#onboarding-tour-screenshots.onboarding-tour-item::after,
#onboarding-notification-bar[data-target-tour-id=onboarding-tour-screenshots] #onboarding-notification-tour-title::before {
background-image: url("img/icons_screenshots.svg");
}
#onboarding-tour-screenshots.onboarding-active,
.onboarding-tour-item-container:hover #onboarding-tour-screenshots {
background-image: url("img/icons_screenshots-colored.svg");
mask-image: url("img/icons_screenshots.svg");
}
a#onboarding-tour-screenshots-button,
@ -551,14 +528,16 @@ a#onboarding-tour-screenshots-button:visited {
#onboarding-notification-tour-title {
margin: 0;
font-weight: bold;
color: #0f1126;
color: var(--onboarding-notification-tour-title-color);
font-size: 14px;
--onboarding-notification-tour-title-color: #0f1126;
}
#onboarding-notification-tour-title::before {
content: "";
background-repeat: no-repeat;
background-size: 14px;
background-color: var(--onboarding-notification-tour-title-color);
mask-repeat: no-repeat;
mask-size: 14px;
height: 16px;
width: 16px;
float: inline-start;

Просмотреть файл

@ -301,15 +301,6 @@
text-align: start;
}
.requests-list-icon {
background: transparent;
width: 15px;
height: 15px;
margin: 0 4px;
outline: 1px solid var(--table-splitter-color);
vertical-align: top;
}
/* Protocol column */
.requests-list-protocol {

Просмотреть файл

@ -11,10 +11,9 @@ const {
} = require("devtools/client/shared/vendor/react");
const { propertiesEqual } = require("../utils/request-utils");
const { div, img } = DOM;
const { div } = DOM;
const UPDATED_FILE_PROPS = [
"responseContentDataUri",
"urlDetails",
];
@ -22,7 +21,6 @@ class RequestListColumnFile extends Component {
static get propTypes() {
return {
item: PropTypes.object.isRequired,
onThumbnailMouseDown: PropTypes.func.isRequired,
};
}
@ -32,8 +30,7 @@ class RequestListColumnFile extends Component {
render() {
let {
item: { responseContentDataUri, urlDetails },
onThumbnailMouseDown
item: { urlDetails },
} = this.props;
return (
@ -41,11 +38,6 @@ class RequestListColumnFile extends Component {
className: "requests-list-column requests-list-file",
title: urlDetails.unicodeUrl,
},
img({
className: "requests-list-icon",
src: responseContentDataUri,
onMouseDown: onThumbnailMouseDown,
}),
urlDetails.baseNameWithQuery
)
);

Просмотреть файл

@ -47,7 +47,6 @@ class RequestListContent extends Component {
onItemMouseDown: PropTypes.func.isRequired,
onSecurityIconMouseDown: PropTypes.func.isRequired,
onSelectDelta: PropTypes.func.isRequired,
onThumbnailMouseDown: PropTypes.func.isRequired,
onWaterfallMouseDown: PropTypes.func.isRequired,
scale: PropTypes.number,
selectedRequestId: PropTypes.string,
@ -71,6 +70,7 @@ class RequestListContent extends Component {
getTabTarget: connector.getTabTarget,
getLongString: connector.getLongString,
openStatistics: (open) => dispatch(Actions.openStatistics(connector, open)),
requestData: connector.requestData,
});
this.tooltip = new HTMLTooltip(window.parent.document, { type: "arrow" });
}
@ -148,7 +148,7 @@ class RequestListContent extends Component {
}
let { connector } = this.props;
if (requestItem.responseContent && target.closest(".requests-list-icon")) {
if (target.closest(".requests-list-file")) {
return setTooltipImageContent(connector, tooltip, itemEl, requestItem);
}
@ -221,7 +221,6 @@ class RequestListContent extends Component {
onCauseBadgeMouseDown,
onItemMouseDown,
onSecurityIconMouseDown,
onThumbnailMouseDown,
onWaterfallMouseDown,
scale,
selectedRequestId,
@ -251,7 +250,6 @@ class RequestListContent extends Component {
onMouseDown: () => onItemMouseDown(item.id),
onCauseBadgeMouseDown: () => onCauseBadgeMouseDown(item.cause),
onSecurityIconMouseDown: () => onSecurityIconMouseDown(item.securityState),
onThumbnailMouseDown: () => onThumbnailMouseDown(),
onWaterfallMouseDown: () => onWaterfallMouseDown(),
}))
)
@ -290,13 +288,6 @@ module.exports = connect(
}
},
onSelectDelta: (delta) => dispatch(Actions.selectDelta(delta)),
/**
* A handler that opens the response tab in the details view if
* the thumbnail is clicked.
*/
onThumbnailMouseDown: () => {
dispatch(Actions.selectDetailsPanelTab("response"));
},
/**
* A handler that opens the timing sidebar panel if the waterfall is clicked.
*/

Просмотреть файл

@ -48,7 +48,6 @@ const UPDATED_REQ_ITEM_PROPS = [
"mimeType",
"eventTimings",
"securityState",
"responseContentDataUri",
"status",
"statusText",
"fromCache",
@ -87,7 +86,6 @@ class RequestListItem extends Component {
onFocusedNodeChange: PropTypes.func,
onMouseDown: PropTypes.func.isRequired,
onSecurityIconMouseDown: PropTypes.func.isRequired,
onThumbnailMouseDown: PropTypes.func.isRequired,
onWaterfallMouseDown: PropTypes.func.isRequired,
waterfallWidth: PropTypes.number,
};
@ -126,7 +124,6 @@ class RequestListItem extends Component {
onMouseDown,
onCauseBadgeMouseDown,
onSecurityIconMouseDown,
onThumbnailMouseDown,
onWaterfallMouseDown,
} = this.props;
@ -145,7 +142,7 @@ class RequestListItem extends Component {
},
columns.get("status") && RequestListColumnStatus({ item }),
columns.get("method") && RequestListColumnMethod({ item }),
columns.get("file") && RequestListColumnFile({ item, onThumbnailMouseDown }),
columns.get("file") && RequestListColumnFile({ item }),
columns.get("protocol") && RequestListColumnProtocol({ item }),
columns.get("scheme") && RequestListColumnScheme({ item }),
columns.get("domain") && RequestListColumnDomain({ item,

Просмотреть файл

@ -41,6 +41,7 @@ class ResponsePanel extends Component {
return {
request: PropTypes.object.isRequired,
openLink: PropTypes.func,
connector: PropTypes.object.isRequired,
};
}
@ -58,6 +59,36 @@ class ResponsePanel extends Component {
this.isJSON = this.isJSON.bind(this);
}
/**
* `componentDidMount` is called when opening the ResponsePanel for the first time
*/
componentDidMount() {
this.maybeFetchResponseContent(this.props);
}
/**
* `componentWillReceiveProps` is the only method called when switching between two
* requests while the response panel is displayed.
*/
componentWillReceiveProps(nextProps) {
this.maybeFetchResponseContent(nextProps);
}
/**
* When switching to another request, lazily fetch response content
* from the backend. The Response Panel will first be empty and then
* display the content.
*/
maybeFetchResponseContent(props) {
if (props.request.responseContentAvailable &&
(!props.request.responseContent ||
!props.request.responseContent.content)) {
// This method will set `props.request.responseContent.content`
// asynchronously and force another render.
props.connector.requestData(props.request.id, "responseContent");
}
}
updateImageDimemsions({ target }) {
this.setState({
imageDimensions: {

Просмотреть файл

@ -78,7 +78,7 @@ function TabboxPanel({
id: PANELS.RESPONSE,
title: RESPONSE_TITLE,
},
ResponsePanel({ request, openLink }),
ResponsePanel({ request, openLink, connector }),
),
TabPanel({
id: PANELS.TIMINGS,

Просмотреть файл

@ -12,12 +12,12 @@ const {
PropTypes,
} = require("devtools/client/shared/vendor/react");
const { connect } = require("devtools/client/shared/vendor/react-redux");
const I = require("devtools/client/shared/vendor/immutable");
const Actions = require("../actions/index");
const { FILTER_SEARCH_DELAY, FILTER_TAGS } = require("../constants");
const {
getRecordingState,
getRequestFilterTypes,
getTypeFilteredRequests,
isNetworkDetailsToggleButtonDisabled,
} = require("../selectors/index");
@ -62,7 +62,7 @@ class Toolbar extends Component {
toggleRecording: PropTypes.func.isRequired,
recording: PropTypes.bool.isRequired,
clearRequests: PropTypes.func.isRequired,
requestFilterTypes: PropTypes.array.isRequired,
requestFilterTypes: PropTypes.object.isRequired,
setRequestFilterText: PropTypes.func.isRequired,
networkDetailsToggleDisabled: PropTypes.bool.isRequired,
networkDetailsOpen: PropTypes.bool.isRequired,
@ -93,6 +93,18 @@ class Toolbar extends Component {
this.updateBrowserCacheDisabled);
}
shouldComponentUpdate(nextProps) {
return this.props.networkDetailsOpen !== nextProps.networkDetailsOpen
|| this.props.networkDetailsToggleDisabled !== nextProps.networkDetailsToggleDisabled
|| this.props.persistentLogsEnabled !== nextProps.persistentLogsEnabled
|| this.props.browserCacheDisabled !== nextProps.browserCacheDisabled
|| this.props.recording !== nextProps.recording
|| !I.is(this.props.requestFilterTypes, nextProps.requestFilterTypes)
// Filtered requests are useful only when searchbox is focused
|| this.refs.searchbox && this.refs.searchbox.focused;
}
componentWillUnmount() {
Services.prefs.removeObserver(DEVTOOLS_ENABLE_PERSISTENT_LOG_PREF,
this.updatePersistentLogsEnabled);
@ -138,7 +150,7 @@ class Toolbar extends Component {
} = this.props;
// Render list of filter-buttons.
let buttons = requestFilterTypes.map(([type, checked]) => {
let buttons = requestFilterTypes.entrySeq().toArray().map(([type, checked]) => {
let classList = ["devtools-button", `requests-list-filter-${type}-button`];
checked && classList.push("checked");
@ -227,6 +239,7 @@ class Toolbar extends Component {
keyShortcut: SEARCH_KEY_SHORTCUT,
placeholder: SEARCH_PLACE_HOLDER,
type: "filter",
ref: "searchbox",
onChange: setRequestFilterText,
autocompleteProvider: this.autocompleteProvider,
}),
@ -251,7 +264,7 @@ module.exports = connect(
networkDetailsOpen: state.ui.networkDetailsOpen,
persistentLogsEnabled: state.ui.persistentLogsEnabled,
recording: getRecordingState(state),
requestFilterTypes: getRequestFilterTypes(state),
requestFilterTypes: state.filters.requestFilterTypes,
}),
(dispatch) => ({
clearRequests: () => dispatch(Actions.clearRequests()),

Просмотреть файл

@ -4,8 +4,6 @@
"use strict";
const { formDataURI } = require("../../utils/request-utils");
function ResponseInfo(id, response, content) {
let {
mimeType
@ -23,7 +21,7 @@ function ResponseInfo(id, response, content) {
}
function ResponseContent(id, response, content) {
const {body, base64Encoded} = content;
const {body} = content;
let {mimeType, encodedDataLength} = response;
let responseContent = ResponseInfo(id, response, content);
let payload = Object.assign(
@ -33,9 +31,6 @@ function ResponseContent(id, response, content) {
transferredSize: encodedDataLength, // TODO: verify
mimeType: mimeType
}, body);
if (mimeType.includes("image/")) {
payload.responseContentDataUri = formDataURI(mimeType, base64Encoded, response);
}
return payload;
}

Просмотреть файл

@ -22,6 +22,7 @@ class FirefoxConnector {
this.triggerActivity = this.triggerActivity.bind(this);
this.getTabTarget = this.getTabTarget.bind(this);
this.viewSourceInDebugger = this.viewSourceInDebugger.bind(this);
this.requestData = this.requestData.bind(this);
// Internals
this.getLongString = this.getLongString.bind(this);
@ -285,6 +286,10 @@ class FirefoxConnector {
this.toolbox.viewSourceInDebugger(sourceURL, sourceLine);
}
}
requestData(request, type) {
return this.dataProvider.requestData(request, type);
}
}
module.exports = new FirefoxConnector();

Просмотреть файл

@ -9,7 +9,6 @@ const { EVENTS } = require("../constants");
const { CurlUtils } = require("devtools/client/shared/curl");
const {
fetchHeaders,
formDataURI,
} = require("../utils/request-utils");
/**
@ -30,6 +29,10 @@ class FirefoxDataProvider {
this.payloadQueue = [];
this.rdpRequestMap = new Map();
// Map[key string => Promise] used by `requestData` to prevent requesting the same
// request data twice.
this.lazyRequestData = new Map();
// Fetching data from the backend
this.getLongString = this.getLongString.bind(this);
@ -117,18 +120,15 @@ class FirefoxDataProvider {
);
this.pushRequestToQueue(id, payload);
return payload;
}
async fetchResponseContent(mimeType, responseContent) {
let payload = {};
if (mimeType && responseContent && responseContent.content) {
let { encoding, text } = responseContent.content;
let { text } = responseContent.content;
let response = await this.getLongString(text);
if (mimeType.includes("image/")) {
payload.responseContentDataUri = formDataURI(mimeType, encoding, response);
}
responseContent.content.text = response;
payload.responseContent = responseContent;
}
@ -243,10 +243,24 @@ class FirefoxDataProvider {
return false;
}
// The payload is ready when all values in the record are true.
// (i.e. all data received).
let props = Object.getOwnPropertyNames(record);
return props.every(prop => record[prop] === true);
let { payload } = this.getRequestFromQueue(id);
// The payload is ready when all values in the record are true. (i.e. all data
// received, but the lazy one. responseContent is the only one for now).
// Note that we never fetch response header/cookies for request with security issues.
// (Be careful, securityState can be undefined, for example for WebSocket requests)
// Also note that service worker don't have security info set.
// Bug 1404917 should simplify this heuristic by making all these field be lazily
// fetched, only on-demand.
return record.requestHeaders &&
record.requestCookies &&
record.eventTimings &&
(record.securityInfo || record.fromServiceWorker) &&
(
(record.responseHeaders && record.responseCookies) ||
payload.securityState == "broken" ||
(payload.responseContentAvailable && !payload.status)
);
}
/**
@ -319,8 +333,14 @@ class FirefoxDataProvider {
this.rdpRequestMap.set(actor, {
requestHeaders: false,
requestCookies: false,
responseHeaders: false,
responseCookies: false,
securityInfo: false,
eventTimings: false,
responseContent: false,
// This isn't a request data, but we need to know about request being served from
// service worker later, from isRequestPayloadReady.
fromServiceWorker,
});
this.addRequest(actor, {
@ -348,51 +368,25 @@ class FirefoxDataProvider {
let { actor } = networkInfo;
let { updateType } = packet;
// When we pause and resume, we may receive `networkEventUpdate` for a request
// that started during the pause and we missed its `networkEvent`.
if (!this.rdpRequestMap.has(actor)) {
return;
}
switch (updateType) {
case "requestHeaders":
this.requestData(actor, updateType).then(response => {
this.onRequestHeaders(response)
.then(() => this.onDataReceived(actor, updateType));
emit(EVENTS.UPDATING_REQUEST_HEADERS, actor);
});
break;
case "requestCookies":
this.requestData(actor, updateType).then(response => {
this.onRequestCookies(response)
.then(() => this.onDataReceived(actor, updateType));
emit(EVENTS.UPDATING_REQUEST_COOKIES, actor);
});
break;
case "requestPostData":
this.requestData(actor, updateType).then(response => {
this.onRequestPostData(response)
.then(() => this.onDataReceived(actor, updateType));
emit(EVENTS.UPDATING_REQUEST_POST_DATA, actor);
});
case "responseHeaders":
case "responseCookies":
this.requestPayloadData(actor, updateType);
break;
case "securityInfo":
this.updateRequest(actor, {
securityState: networkInfo.securityInfo,
}).then(() => {
this.requestData(actor, updateType).then(response => {
this.onSecurityInfo(response)
.then(() => this.onDataReceived(actor, updateType));
emit(EVENTS.UPDATING_SECURITY_INFO, actor);
});
});
break;
case "responseHeaders":
this.requestData(actor, updateType).then(response => {
this.onResponseHeaders(response)
.then(() => this.onDataReceived(actor, updateType));
emit(EVENTS.UPDATING_RESPONSE_HEADERS, actor);
});
break;
case "responseCookies":
this.requestData(actor, updateType).then(response => {
this.onResponseCookies(response)
.then(() => this.onDataReceived(actor, updateType));
emit(EVENTS.UPDATING_RESPONSE_COOKIES, actor);
this.requestPayloadData(actor, updateType);
});
break;
case "responseStart":
@ -408,23 +402,20 @@ class FirefoxDataProvider {
});
break;
case "responseContent":
this.requestData(actor, updateType).then(response => {
this.onResponseContent({
contentSize: networkInfo.response.bodySize,
transferredSize: networkInfo.response.transferredSize,
mimeType: networkInfo.response.content.mimeType
}, response).then(() => this.onDataReceived(actor, updateType));
emit(EVENTS.UPDATING_RESPONSE_CONTENT, actor);
this.updateRequest(actor, {
contentSize: networkInfo.response.bodySize,
transferredSize: networkInfo.response.transferredSize,
mimeType: networkInfo.response.content.mimeType,
// This field helps knowing when/if responseContent property is available
// and can be requested via `requestData`
responseContentAvailable: true,
});
break;
case "eventTimings":
this.updateRequest(actor, { totalTime: networkInfo.totalTime })
.then(() => {
this.requestData(actor, updateType).then(response => {
this.onEventTimings(response)
.then(() => this.onDataReceived(actor, updateType));
emit(EVENTS.UPDATING_EVENT_TIMINGS, actor);
});
this.requestPayloadData(actor, updateType);
});
break;
}
@ -433,65 +424,43 @@ class FirefoxDataProvider {
}
/**
* Wrapper method for requesting HTTP details data from the backend.
* Wrapper method for requesting HTTP details data for the payload.
*
* It collects all RDP requests and monitors responses, so it's
* possible to determine whether (and when) all requested data
* has been fetched from the backend.
* It is specific to all requests done from `onNetworkEventUpdate`, for data that are
* immediately fetched whenever the data is available.
*
* It also nicely returns a promise.
* All these requests are cached into `rdpRequestMap`. All requests related to a given
* actor will be collected in the same record.
*
* Once bug 1404917 is completed, we should no longer use this method.
* All request fields should be loaded only on-demand, via `requestData` method.
*
* @param {string} actor actor id (used as request id)
* @param {string} method identifier of the data we want to fetch
*
* @return {Promise} return a promise resolved when data are received.
*/
requestData(actor, method) {
requestPayloadData(actor, method) {
let record = this.rdpRequestMap.get(actor);
// All RDP requests related to the given actor will be collected
// in the same record.
if (!record) {
record = {};
// If data has been already requested, do nothing.
if (record[method]) {
return;
}
// If data has been already requested return the same promise.
if (record.method) {
return record.method;
}
// Calculate real name of the client getter.
let realMethodName = "get" + method.charAt(0).toUpperCase() +
method.slice(1);
// Request data from the backend.
let promise = new Promise((resolve, reject) => {
if (typeof this.webConsoleClient[realMethodName] == "function") {
this.webConsoleClient[realMethodName](actor, response => {
// Resolve incoming HTTP details data-promise.
resolve(response);
});
} else {
reject(new Error("Error: No such client method!"));
}
let promise = this._requestData(actor, method);
promise.then(() => {
// Once we got the data toggle the Map item to `true` in order to
// make isRequestPayloadReady return `true` once all the data is fetched.
record[method] = true;
this.onPayloadDataReceived(actor, method, !record);
});
// Store the promise in order to know about RDP requests
// in progress.
record[method] = promise;
return promise;
}
/**
* Executed when new data are received from the backend.
*/
async onDataReceived(actor, type) {
let record = this.rdpRequestMap.get(actor);
if (record) {
record[type] = true;
}
async onPayloadDataReceived(actor, type) {
// Notify actions when all the sync request from onNetworkEventUpdate are done,
// or, everytime requestData is called for fetching data lazily.
if (this.isRequestPayloadReady(actor)) {
let payloadFromQueue = this.getRequestFromQueue(actor).payload;
@ -504,10 +473,88 @@ class FirefoxDataProvider {
await updateRequest(actor, payloadFromQueue, true);
}
// This event is fired only once per request, once all the properties are fetched
// from `onNetworkEventUpdate`. There should be no more RDP requests after this.
emit(EVENTS.PAYLOAD_READY, actor);
}
}
/**
* Public connector API to lazily request HTTP details from the backend.
*
* This is internal method that focus on:
* - calling the right actor method,
* - emitting an event to tell we start fetching some request data,
* - call data processing method.
*
* @param {string} actor actor id (used as request id)
* @param {string} method identifier of the data we want to fetch
*
* @return {Promise} return a promise resolved when data is received.
*/
requestData(actor, method) {
// Key string used in `lazyRequestData`. We use this Map to prevent requesting
// the same data twice at the same time.
let key = actor + "-" + method;
let promise = this.lazyRequestData.get(key);
// If a request is pending, reuse it.
if (promise) {
return promise;
}
// Fetch the data
promise = this._requestData(actor, method);
this.lazyRequestData.set(key, promise);
promise.then(async () => {
// Remove the request from the cache, any new call to requestData will fetch the
// data again.
this.lazyRequestData.delete(key, promise);
let payloadFromQueue = this.getRequestFromQueue(actor).payload;
let { updateRequest } = this.actions;
if (updateRequest) {
await updateRequest(actor, payloadFromQueue, true);
}
});
return promise;
}
/**
* Internal helper used to request HTTP details from the backend.
*
* This is internal method that focus on:
* - calling the right actor method,
* - emitting an event to tell we start fetching some request data,
* - call data processing method.
*
* @param {string} actor actor id (used as request id)
* @param {string} method identifier of the data we want to fetch
*
* @return {Promise} return a promise resolved when data is received.
*/
async _requestData(actor, method) {
// Calculate real name of the client getter.
let clientMethodName = "get" + method.charAt(0).toUpperCase() +
method.slice(1);
// The name of the callback that processes request response
let callbackMethodName = "on" + method.charAt(0).toUpperCase() +
method.slice(1);
// And the event to fire before updating this data
let updatingEventName = "UPDATING_" + method.replace(/([A-Z])/g, "_$1").toUpperCase();
if (typeof this.webConsoleClient[clientMethodName] == "function") {
// Emit event that tell we just start fetching some data
emit(EVENTS[updatingEventName], actor);
// Do a RDP request to fetch data from the actor.
let response = await this.webConsoleClient[clientMethodName](actor);
// Call data processing method.
response = await this[callbackMethodName](response);
return response;
}
throw new Error("Error: No such client method '" + clientMethodName + "'!");
}
/**
* Handles additional information received for a "requestHeaders" packet.
*
@ -587,16 +634,20 @@ class FirefoxDataProvider {
}
/**
* Handles additional information received for a "responseContent" packet.
* Handles additional information received via "getResponseContent" request.
*
* @param {object} data the message received from the server event.
* @param {object} response the message received from the server.
*/
onResponseContent(data, response) {
let payload = Object.assign({ responseContent: response }, data);
return this.updateRequest(response.from, payload).then(() => {
emit(EVENTS.RECEIVED_RESPONSE_CONTENT, response.from);
async onResponseContent(response) {
let payload = await this.updateRequest(response.from, {
// We have to ensure passing mimeType as fetchResponseContent needs it from
// updateRequest. It will convert the LongString in `response.content.text` to a
// string.
mimeType: response.content.mimeType,
responseContent: response,
});
emit(EVENTS.RECEIVED_RESPONSE_CONTENT, response.from);
return payload.responseContent;
}
/**

Просмотреть файл

@ -25,6 +25,7 @@ class Connector {
this.setPreferences = this.setPreferences.bind(this);
this.triggerActivity = this.triggerActivity.bind(this);
this.viewSourceInDebugger = this.viewSourceInDebugger.bind(this);
this.requestData = this.requestData.bind(this);
}
// Connect/Disconnect API
@ -98,6 +99,10 @@ class Connector {
viewSourceInDebugger() {
return this.connector.viewSourceInDebugger(...arguments);
}
requestData() {
return this.connector.requestData(...arguments);
}
}
module.exports.Connector = Connector;

Просмотреть файл

@ -124,7 +124,7 @@ const UPDATE_PROPS = [
"responseHeaders",
"responseCookies",
"responseContent",
"responseContentDataUri",
"responseContentAvailable",
"formDataSections",
];

Просмотреть файл

@ -168,6 +168,7 @@ HarAutomation.prototype = {
let title = form.title || form.url;
let options = {
requestData: null,
getString: this.getString.bind(this),
view: this,
items: items,

Просмотреть файл

@ -51,7 +51,7 @@ HarBuilder.prototype = {
* @returns {Promise} A promise that resolves to the HAR object when
* the entire build process is done.
*/
build: function () {
build: async function () {
this.promises = [];
// Build basic structure for data.
@ -59,12 +59,14 @@ HarBuilder.prototype = {
// Build entries.
for (let file of this._options.items) {
log.entries.push(this.buildEntry(log, file));
log.entries.push(await this.buildEntry(log, file));
}
// Some data needs to be fetched from the backend during the
// build process, so wait till all is done.
return Promise.all(this.promises).then(() => ({ log }));
await Promise.all(this.promises);
return { log };
},
// Helpers
@ -110,7 +112,7 @@ HarBuilder.prototype = {
return page;
},
buildEntry: function (log, file) {
buildEntry: async function (log, file) {
let page = this.getPage(log, file);
let entry = {};
@ -119,7 +121,7 @@ HarBuilder.prototype = {
entry.time = file.endedMillis - file.startedMillis;
entry.request = this.buildRequest(file);
entry.response = this.buildResponse(file);
entry.response = await this.buildResponse(file);
entry.cache = this.buildCache(file);
entry.timings = file.eventTimings ? file.eventTimings.timings : {};
@ -286,7 +288,7 @@ HarBuilder.prototype = {
return postData;
},
buildResponse: function (file) {
buildResponse: async function (file) {
let response = {
status: 0
};
@ -303,7 +305,7 @@ HarBuilder.prototype = {
response.headers = this.buildHeaders(responseHeaders);
response.cookies = this.buildCookies(file.responseCookies);
response.content = this.buildContent(file);
response.content = await this.buildContent(file);
let headers = responseHeaders ? responseHeaders.headers : null;
let headersSize = responseHeaders ? responseHeaders.headersSize : -1;
@ -323,13 +325,19 @@ HarBuilder.prototype = {
return response;
},
buildContent: function (file) {
buildContent: async function (file) {
let content = {
mimeType: file.mimeType,
size: -1
};
// When using HarAutomation, HarCollector will automatically fetch responseContent,
// but when we use it from netmonitor, FirefoxDataProvider should fetch it itself
// lazily, via requestData.
let responseContent = file.responseContent;
if (!responseContent && this._options.requestData) {
responseContent = await this._options.requestData(file.id, "responseContent");
}
if (responseContent && responseContent.content) {
content.size = responseContent.content.size;
content.encoding = responseContent.content.encoding;

Просмотреть файл

@ -6,6 +6,8 @@ support-files =
head.js
html_har_post-data-test-page.html
!/devtools/client/netmonitor/test/head.js
!/devtools/client/framework/test/shared-head.js
!/devtools/client/netmonitor/test/shared-head.js
!/devtools/client/netmonitor/test/html_simple-test-page.html
[browser_net_har_copy_all_as_har.js]

Просмотреть файл

@ -19,7 +19,7 @@ add_task(function* () {
let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
let RequestListContextMenu = windowRequire(
"devtools/client/netmonitor/src/request-list-context-menu");
let { getLongString, getTabTarget } = connector;
let { getLongString, getTabTarget, requestData } = connector;
store.dispatch(Actions.batchEnable(false));
@ -27,7 +27,8 @@ add_task(function* () {
tab.linkedBrowser.reload();
yield wait;
let contextMenu = new RequestListContextMenu({ getTabTarget, getLongString });
let contextMenu = new RequestListContextMenu({
getTabTarget, getLongString, requestData });
yield contextMenu.copyAllAsHar();

Просмотреть файл

@ -16,7 +16,7 @@ add_task(function* () {
let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
let RequestListContextMenu = windowRequire(
"devtools/client/netmonitor/src/request-list-context-menu");
let { getLongString, getTabTarget } = connector;
let { getLongString, getTabTarget, requestData } = connector;
store.dispatch(Actions.batchEnable(false));
@ -28,7 +28,8 @@ add_task(function* () {
yield wait;
// Copy HAR into the clipboard (asynchronous).
let contextMenu = new RequestListContextMenu({ getTabTarget, getLongString });
let contextMenu = new RequestListContextMenu({
getTabTarget, getLongString, requestData });
let jsonString = yield contextMenu.copyAllAsHar();
let har = JSON.parse(jsonString);

Просмотреть файл

@ -16,7 +16,7 @@ add_task(function* () {
let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
let RequestListContextMenu = windowRequire(
"devtools/client/netmonitor/src/request-list-context-menu");
let { getLongString, getTabTarget } = connector;
let { getLongString, getTabTarget, requestData } = connector;
store.dispatch(Actions.batchEnable(false));
@ -28,7 +28,8 @@ add_task(function* () {
yield wait;
// Copy HAR into the clipboard (asynchronous).
let contextMenu = new RequestListContextMenu({ getTabTarget, getLongString });
let contextMenu = new RequestListContextMenu({
getTabTarget, getLongString, requestData });
let jsonString = yield contextMenu.copyAllAsHar();
let har = JSON.parse(jsonString);

Просмотреть файл

@ -20,7 +20,7 @@ function* throttleUploadTest(actuallyThrottle) {
let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
let RequestListContextMenu = windowRequire(
"devtools/client/netmonitor/src/request-list-context-menu");
let { getLongString, getTabTarget, setPreferences } = connector;
let { getLongString, getTabTarget, setPreferences, requestData } = connector;
store.dispatch(Actions.batchEnable(false));
@ -53,7 +53,8 @@ function* throttleUploadTest(actuallyThrottle) {
yield wait;
// Copy HAR into the clipboard (asynchronous).
let contextMenu = new RequestListContextMenu({ getTabTarget, getLongString });
let contextMenu = new RequestListContextMenu({
getTabTarget, getLongString, requestData });
let jsonString = yield contextMenu.copyAllAsHar();
let har = JSON.parse(jsonString);

Просмотреть файл

@ -13,7 +13,6 @@ const {
ENABLE_PERSISTENT_LOGS,
DISABLE_BROWSER_CACHE,
} = require("../constants");
const { getRequestFilterTypes } = require("../selectors/index");
/**
* Update the relevant prefs when:
@ -26,7 +25,8 @@ function prefsMiddleware(store) {
switch (action.type) {
case ENABLE_REQUEST_FILTER_TYPE_ONLY:
case TOGGLE_REQUEST_FILTER_TYPE:
let filters = getRequestFilterTypes(store.getState())
let filters = store.getState().filters.requestFilterTypes
.entrySeq().toArray()
.filter(([type, check]) => check)
.map(([type, check]) => type);
Services.prefs.setCharPref(

Просмотреть файл

@ -58,7 +58,7 @@ const Request = I.Record({
responseHeaders: undefined,
responseCookies: undefined,
responseContent: undefined,
responseContentDataUri: undefined,
responseContentAvailable: false,
formDataSections: undefined,
});

Просмотреть файл

@ -20,6 +20,7 @@ const {
getUrlQuery,
parseQueryString,
getUrlBaseName,
formDataURI,
} = require("./utils/request-utils");
function RequestListContextMenu({
@ -27,11 +28,13 @@ function RequestListContextMenu({
getLongString,
getTabTarget,
openStatistics,
requestData,
}) {
this.cloneSelectedRequest = cloneSelectedRequest;
this.getLongString = getLongString;
this.getTabTarget = getTabTarget;
this.openStatistics = openStatistics;
this.requestData = requestData;
}
RequestListContextMenu.prototype = {
@ -114,10 +117,7 @@ RequestListContextMenu.prototype = {
id: "request-list-context-copy-response",
label: L10N.getStr("netmonitor.context.copyResponse"),
accesskey: L10N.getStr("netmonitor.context.copyResponse.accesskey"),
visible: !!(selectedRequest &&
selectedRequest.responseContent &&
selectedRequest.responseContent.content.text &&
selectedRequest.responseContent.content.text.length !== 0),
visible: !!(selectedRequest && selectedRequest.responseContentAvailable),
click: () => this.copyResponse(),
});
@ -126,8 +126,8 @@ RequestListContextMenu.prototype = {
label: L10N.getStr("netmonitor.context.copyImageAsDataUri"),
accesskey: L10N.getStr("netmonitor.context.copyImageAsDataUri.accesskey"),
visible: !!(selectedRequest &&
selectedRequest.responseContent &&
selectedRequest.responseContent.content.mimeType.includes("image/")),
selectedRequest.mimeType &&
selectedRequest.mimeType.includes("image/")),
click: () => this.copyImageAsDataUri(),
});
@ -164,8 +164,8 @@ RequestListContextMenu.prototype = {
label: L10N.getStr("netmonitor.context.saveImageAs"),
accesskey: L10N.getStr("netmonitor.context.saveImageAs.accesskey"),
visible: !!(selectedRequest &&
selectedRequest.responseContent &&
selectedRequest.responseContent.content.mimeType.includes("image/")),
selectedRequest.mimeType &&
selectedRequest.mimeType.includes("image/")),
click: () => this.saveImageAs(),
});
@ -200,8 +200,8 @@ RequestListContextMenu.prototype = {
label: L10N.getStr("netmonitor.context.openInDebugger"),
accesskey: L10N.getStr("netmonitor.context.openInDebugger.accesskey"),
visible: !!(selectedRequest &&
selectedRequest.responseContent &&
selectedRequest.responseContent.content.mimeType.includes("javascript")),
selectedRequest.mimeType &&
selectedRequest.mimeType.includes("javascript")),
click: () => this.openInDebugger()
});
@ -210,9 +210,9 @@ RequestListContextMenu.prototype = {
label: L10N.getStr("netmonitor.context.openInStyleEditor"),
accesskey: L10N.getStr("netmonitor.context.openInStyleEditor.accesskey"),
visible: !!(selectedRequest &&
selectedRequest.responseContent &&
Services.prefs.getBoolPref("devtools.styleeditor.enabled") &&
selectedRequest.responseContent.content.mimeType.includes("css")),
selectedRequest.mimeType &&
selectedRequest.mimeType.includes("css")),
click: () => this.openInStyleEditor()
});
@ -338,15 +338,22 @@ RequestListContextMenu.prototype = {
/**
* Copy image as data uri.
*/
copyImageAsDataUri() {
copyString(this.selectedRequest.responseContentDataUri);
async copyImageAsDataUri() {
let responseContent = await this.requestData(this.selectedRequest.id,
"responseContent");
let { mimeType } = this.selectedRequest;
let { encoding, text } = responseContent.content;
let src = formDataURI(mimeType, encoding, text);
copyString(src);
},
/**
* Save image as.
*/
saveImageAs() {
let { encoding, text } = this.selectedRequest.responseContent.content;
async saveImageAs() {
let responseContent = await this.requestData(this.selectedRequest.id,
"responseContent");
let { encoding, text } = responseContent.content;
let fileName = getUrlBaseName(this.selectedRequest.url);
let data;
if (encoding === "base64") {
@ -364,8 +371,10 @@ RequestListContextMenu.prototype = {
/**
* Copy response data as a string.
*/
copyResponse() {
copyString(this.selectedRequest.responseContent.content.text);
async copyResponse() {
let responseContent = await this.requestData(this.selectedRequest.id,
"responseContent");
copyString(responseContent.content.text);
},
/**
@ -391,6 +400,7 @@ RequestListContextMenu.prototype = {
let title = form.title || form.url;
return {
requestData: this.requestData,
getString: this.getLongString,
items: this.sortedRequests,
title: title

Просмотреть файл

@ -13,20 +13,21 @@ const { formDataURI } = require("./utils/request-utils");
const REQUESTS_TOOLTIP_IMAGE_MAX_DIM = 400; // px
async function setTooltipImageContent(connector, tooltip, itemEl, requestItem) {
let { mimeType, text, encoding } = requestItem.responseContent.content;
let { mimeType } = requestItem;
if (!mimeType || !mimeType.includes("image/")) {
return false;
}
let string = await connector.getLongString(text);
let src = formDataURI(mimeType, encoding, string);
let responseContent = await connector.requestData(requestItem.id, "responseContent");
let { encoding, text } = responseContent.content;
let src = formDataURI(mimeType, encoding, text);
let maxDim = REQUESTS_TOOLTIP_IMAGE_MAX_DIM;
let { naturalWidth, naturalHeight } = await getImageDimensions(tooltip.doc, src);
let options = { maxDim, naturalWidth, naturalHeight };
setImageTooltip(tooltip, tooltip.doc, src, options);
return itemEl.querySelector(".requests-list-icon");
return itemEl.querySelector(".requests-list-file");
}
module.exports = {

Просмотреть файл

@ -1,13 +0,0 @@
/* 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/. */
"use strict";
function getRequestFilterTypes(state) {
return state.filters.requestFilterTypes.entrySeq().toArray();
}
module.exports = {
getRequestFilterTypes,
};

Просмотреть файл

@ -4,13 +4,11 @@
"use strict";
const filters = require("./filters");
const requests = require("./requests");
const timingMarkers = require("./timing-markers");
const ui = require("./ui");
Object.assign(exports,
filters,
requests,
timingMarkers,
ui

Просмотреть файл

@ -3,7 +3,6 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
DevToolsModules(
'filters.js',
'index.js',
'requests.js',
'timing-markers.js',

Просмотреть файл

@ -4,6 +4,7 @@ subsuite = devtools
support-files =
dropmarker.svg
head.js
shared-head.js
html_cause-test-page.html
html_content-type-without-cache-test-page.html
html_brotli-test-page.html
@ -121,7 +122,6 @@ skip-if = (os == 'linux' && debug && bits == 32) # Bug 1303439
[browser_net_footer-summary.js]
[browser_net_headers-alignment.js]
[browser_net_headers_sorted.js]
[browser_net_icon-preview.js]
[browser_net_image-tooltip.js]
[browser_net_json-b64.js]
[browser_net_json-null.js]
@ -175,7 +175,6 @@ skip-if = true # Bug 1258809
[browser_net_status-codes.js]
[browser_net_streaming-response.js]
[browser_net_throttle.js]
[browser_net_thumbnail-click.js]
[browser_net_timeline_ticks.js]
skip-if = true # TODO: fix the test
[browser_net_timing-division.js]

Просмотреть файл

@ -10,7 +10,7 @@
add_task(function* () {
requestLongerTimeout(4);
let { monitor } = yield initNetMonitor(INFINITE_GET_URL, true);
let { tab, monitor } = yield initNetMonitor(INFINITE_GET_URL, true);
let { document, windowRequire, store } = monitor.panelWin;
let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
@ -57,6 +57,11 @@ add_task(function* () {
let headersHeight = requestsContainerHeaders.offsetHeight;
is(requestsContainer.scrollTop, headersHeight, "Did not scroll.");
// Stop doing requests.
yield ContentTask.spawn(tab.linkedBrowser, {}, function () {
content.wrappedJSObject.stopRequests();
});
// Done: clean up.
return teardown(monitor);

Просмотреть файл

@ -51,11 +51,13 @@ add_task(function* () {
});
wait = waitForDOM(document, ".CodeMirror-code");
let onResponseContent = monitor.panelWin.once(EVENTS.RECEIVED_RESPONSE_CONTENT);
EventUtils.sendMouseEvent({ type: "click" },
document.querySelector(".network-details-panel-toggle"));
EventUtils.sendMouseEvent({ type: "click" },
document.querySelector("#response-tab"));
yield wait;
yield onResponseContent;
yield testResponse("br");
yield teardown(monitor);

Просмотреть файл

@ -13,7 +13,6 @@ add_task(function* () {
let { document, store, windowRequire } = monitor.panelWin;
let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
let { EVENTS } = windowRequire("devtools/client/netmonitor/src/constants");
let detailsPanelToggleButton = document.querySelector(".network-details-panel-toggle");
let clearButton = document.querySelector(".requests-list-clear-button");
@ -23,9 +22,9 @@ add_task(function* () {
assertNoRequestState();
// Load one request and assert it shows up in the list
let networkEvent = monitor.panelWin.once(EVENTS.NETWORK_EVENT);
let onMonitorUpdated = waitForAllRequestsFinished(monitor);
tab.linkedBrowser.reload();
yield networkEvent;
yield onMonitorUpdated;
assertSingleRequestState();
@ -34,9 +33,9 @@ add_task(function* () {
assertNoRequestState();
// Load a second request and make sure they still show up
networkEvent = monitor.panelWin.once(EVENTS.NETWORK_EVENT);
onMonitorUpdated = waitForAllRequestsFinished(monitor);
tab.linkedBrowser.reload();
yield networkEvent;
yield onMonitorUpdated;
assertSingleRequestState();

Просмотреть файл

@ -22,7 +22,7 @@ add_task(function* () {
store.dispatch(Actions.batchEnable(false));
let wait = waitForNetworkEvents(monitor, CONTENT_TYPE_WITHOUT_CACHE_REQUESTS);
yield ContentTask.spawn(tab.linkedBrowser, {}, function* () {
yield ContentTask.spawn(tab.linkedBrowser, {}, function () {
content.wrappedJSObject.performRequests();
});
yield wait;
@ -142,25 +142,25 @@ add_task(function* () {
}
);
yield selectIndexAndWaitForSourceEditor(0);
yield selectIndexAndWaitForSourceEditor(monitor, 0);
yield testResponseTab("xml");
yield selectIndexAndWaitForSourceEditor(1);
yield selectIndexAndWaitForSourceEditor(monitor, 1);
yield testResponseTab("css");
yield selectIndexAndWaitForSourceEditor(2);
yield selectIndexAndWaitForSourceEditor(monitor, 2);
yield testResponseTab("js");
yield selectIndexAndWaitForJSONView(3);
yield testResponseTab("json");
yield selectIndexAndWaitForSourceEditor(4);
yield selectIndexAndWaitForSourceEditor(monitor, 4);
yield testResponseTab("html");
yield selectIndexAndWaitForImageView(5);
yield testResponseTab("png");
yield selectIndexAndWaitForSourceEditor(6);
yield selectIndexAndWaitForSourceEditor(monitor, 6);
yield testResponseTab("gzip");
yield teardown(monitor);
@ -270,32 +270,26 @@ add_task(function* () {
}
}
function* selectIndexAndWaitForSourceEditor(index) {
let editor = document.querySelector("#response-panel .CodeMirror-code");
if (!editor) {
let waitDOM = waitForDOM(document, "#response-panel .CodeMirror-code");
EventUtils.sendMouseEvent({ type: "mousedown" },
document.querySelectorAll(".request-list-item")[index]);
document.querySelector("#response-tab").click();
yield waitDOM;
} else {
EventUtils.sendMouseEvent({ type: "mousedown" },
document.querySelectorAll(".request-list-item")[index]);
}
}
function* selectIndexAndWaitForJSONView(index) {
let onResponseContent = monitor.panelWin.once(EVENTS.RECEIVED_RESPONSE_CONTENT);
let tabpanel = document.querySelector("#response-panel");
let waitDOM = waitForDOM(tabpanel, ".treeTable");
store.dispatch(Actions.selectRequestByIndex(index));
yield waitDOM;
yield onResponseContent;
// Waiting for RECEIVED_RESPONSE_CONTENT isn't enough.
// DOM may not be fully updated yet and checkVisibility(json) may still fail.
yield waitForTick();
}
function* selectIndexAndWaitForImageView(index) {
let onResponseContent = monitor.panelWin.once(EVENTS.RECEIVED_RESPONSE_CONTENT);
let tabpanel = document.querySelector("#response-panel");
let waitDOM = waitForDOM(tabpanel, ".response-image");
store.dispatch(Actions.selectRequestByIndex(index));
let [imageNode] = yield waitDOM;
yield once(imageNode, "load");
yield onResponseContent;
}
});

Просмотреть файл

@ -10,7 +10,7 @@
add_task(function* () {
requestLongerTimeout(4);
let { monitor } = yield initNetMonitor(INFINITE_GET_URL, true);
let { tab, monitor } = yield initNetMonitor(INFINITE_GET_URL, true);
let { document, windowRequire, store } = monitor.panelWin;
let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
@ -40,6 +40,11 @@ add_task(function* () {
);
}
// Stop doing requests.
yield ContentTask.spawn(tab.linkedBrowser, {}, function* () {
content.wrappedJSObject.stopRequests();
});
// Done: clean up.
return teardown(monitor);

Просмотреть файл

@ -1,68 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
/**
* Tests if image responses show a thumbnail in the requests menu.
*/
add_task(function* () {
let { tab, monitor } = yield initNetMonitor(CONTENT_TYPE_WITHOUT_CACHE_URL);
const SELECTOR = ".requests-list-icon[src]";
info("Starting test... ");
let { document, store, windowRequire, connector } = monitor.panelWin;
let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
let { triggerActivity } = connector;
let { ACTIVITY_TYPE } = windowRequire("devtools/client/netmonitor/src/constants");
store.dispatch(Actions.batchEnable(false));
let wait = waitForNetworkEvents(monitor, CONTENT_TYPE_WITHOUT_CACHE_REQUESTS);
yield performRequests();
yield wait;
yield waitUntil(() => !!document.querySelector(SELECTOR));
info("Checking the image thumbnail when all items are shown.");
checkImageThumbnail();
store.dispatch(Actions.sortBy("contentSize"));
info("Checking the image thumbnail when all items are sorted.");
checkImageThumbnail();
store.dispatch(Actions.toggleRequestFilterType("images"));
info("Checking the image thumbnail when only images are shown.");
checkImageThumbnail();
info("Reloading the debuggee and performing all requests again...");
wait = waitForNetworkEvents(monitor, CONTENT_TYPE_WITHOUT_CACHE_REQUESTS);
yield reloadAndPerformRequests();
yield wait;
yield waitUntil(() => !!document.querySelector(SELECTOR));
info("Checking the image thumbnail after a reload.");
checkImageThumbnail();
yield teardown(monitor);
function performRequests() {
return ContentTask.spawn(tab.linkedBrowser, {}, function* () {
content.wrappedJSObject.performRequests();
});
}
function* reloadAndPerformRequests() {
yield triggerActivity(ACTIVITY_TYPE.RELOAD.WITH_CACHE_ENABLED);
yield performRequests();
}
function checkImageThumbnail() {
is(document.querySelectorAll(SELECTOR).length, 1,
"There should be only one image request with a thumbnail displayed.");
is(document.querySelector(SELECTOR).src, TEST_IMAGE_DATA_URI,
"The image requests-list-icon thumbnail is displayed correctly.");
is(document.querySelector(SELECTOR).hidden, false,
"The image requests-list-icon thumbnail should not be hidden.");
}
});

Просмотреть файл

@ -11,7 +11,6 @@ const IMAGE_TOOLTIP_REQUESTS = 1;
*/
add_task(function* test() {
let { tab, monitor } = yield initNetMonitor(IMAGE_TOOLTIP_URL);
const SELECTOR = ".requests-list-icon[src]";
info("Starting test... ");
let { document, store, windowRequire, connector } = monitor.panelWin;
@ -25,7 +24,6 @@ add_task(function* test() {
let onEvents = waitForNetworkEvents(monitor, IMAGE_TOOLTIP_REQUESTS);
yield performRequests();
yield onEvents;
yield waitUntil(() => !!document.querySelector(SELECTOR));
info("Checking the image thumbnail after a few requests were made...");
yield showTooltipAndVerify(document.querySelectorAll(".request-list-item")[0]);
@ -42,7 +40,6 @@ add_task(function* test() {
yield triggerActivity(ACTIVITY_TYPE.RELOAD.WITH_CACHE_ENABLED);
yield performRequests();
yield onEvents;
yield waitUntil(() => !!document.querySelector(SELECTOR));
info("Checking the image thumbnail after a reload.");
yield showTooltipAndVerify(document.querySelectorAll(".request-list-item")[1]);
@ -66,7 +63,7 @@ add_task(function* test() {
* with the expected content.
*/
function* showTooltipAndVerify(target) {
let anchor = target.querySelector(".requests-list-icon");
let anchor = target.querySelector(".requests-list-file");
yield showTooltipOn(anchor);
info("Tooltip was successfully opened for the image request.");

Просмотреть файл

@ -9,8 +9,6 @@
add_task(function* () {
let { monitor } = yield initNetMonitor(SIMPLE_URL);
let { getRequestFilterTypes } = monitor.panelWin
.windowRequire("devtools/client/netmonitor/src/selectors/index");
let Actions = monitor.panelWin
.windowRequire("devtools/client/netmonitor/src/actions/index");
info("Starting test... ");
@ -34,7 +32,8 @@ add_task(function* () {
newValue: ["html", "css"],
// Getter used to retrieve the current value from the frontend, in order
// to verify that the pref was applied properly.
validateValue: () => getRequestFilterTypes(getState())
validateValue: () => getState().filters.requestFilterTypes
.entrySeq().toArray()
.filter(([type, check]) => check)
.map(([type, check]) => type),
// Predicate used to modify the frontend when setting the new pref value,

Просмотреть файл

@ -56,19 +56,23 @@ add_task(function* () {
yield onRequests;
// Check the resent requests
ITEMS.forEach((item, i) => {
for (let i = 0; i < ITEMS.length; i++) {
let item = ITEMS[i];
is(item.method, METHODS[i], `The ${item.method} request has the right method`);
is(item.url, requestUrl, `The ${item.method} request has the right URL`);
is(item.status, 200, `The ${item.method} response has the right status`);
if (item.method === "POST") {
// Force fetching response content
let responseContent = yield connector.requestData(item.id, "responseContent");
is(item.requestPostData.postData.text, "post-data",
"The POST request has the right POST data");
// eslint-disable-next-line mozilla/no-cpows-in-tests
is(item.responseContent.content.text, "Access-Control-Allow-Origin: *",
is(responseContent.content.text, "Access-Control-Allow-Origin: *",
"The POST response has the right content");
}
});
}
info("Finishing the test");
return teardown(monitor);

Просмотреть файл

@ -47,9 +47,6 @@ add_task(function* () {
"RECEIVED_REQUEST_HEADERS",
"UPDATING_REQUEST_COOKIES",
"RECEIVED_REQUEST_COOKIES",
"STARTED_RECEIVING_RESPONSE",
"UPDATING_RESPONSE_CONTENT",
"RECEIVED_RESPONSE_CONTENT",
"UPDATING_EVENT_TIMINGS",
"RECEIVED_EVENT_TIMINGS",
"UPDATING_SECURITY_INFO",

Просмотреть файл

@ -80,7 +80,7 @@ add_task(function* () {
yield executeRequests(1, "https://example.com" + CORS_SJS_PATH);
yield done;
done = waitForSecurityBrokenNetworkEvent(true);
done = waitForSecurityBrokenNetworkEvent();
info("Requesting a resource over HTTP to localhost.");
yield executeRequests(1, "http://localhost" + CORS_SJS_PATH);
yield done;
@ -95,25 +95,16 @@ add_task(function* () {
* Returns a promise that's resolved once a request with security issues is
* completed.
*/
function waitForSecurityBrokenNetworkEvent(networkError) {
function waitForSecurityBrokenNetworkEvent() {
let awaitedEvents = [
"UPDATING_REQUEST_HEADERS",
"RECEIVED_REQUEST_HEADERS",
"UPDATING_REQUEST_COOKIES",
"RECEIVED_REQUEST_COOKIES",
"STARTED_RECEIVING_RESPONSE",
"UPDATING_RESPONSE_CONTENT",
"RECEIVED_RESPONSE_CONTENT",
"UPDATING_EVENT_TIMINGS",
"RECEIVED_EVENT_TIMINGS",
];
// If the reason for breakage is a network error, then the
// STARTED_RECEIVING_RESPONSE event does not fire.
if (networkError) {
awaitedEvents = awaitedEvents.filter(e => e !== "STARTED_RECEIVING_RESPONSE");
}
let promises = awaitedEvents.map((event) => {
return monitor.panelWin.once(EVENTS[event]);
});

Просмотреть файл

@ -98,9 +98,6 @@ add_task(function* () {
"RECEIVED_REQUEST_HEADERS",
"UPDATING_REQUEST_COOKIES",
"RECEIVED_REQUEST_COOKIES",
"STARTED_RECEIVING_RESPONSE",
"UPDATING_RESPONSE_CONTENT",
"RECEIVED_RESPONSE_CONTENT",
"UPDATING_EVENT_TIMINGS",
"RECEIVED_EVENT_TIMINGS",
];

Просмотреть файл

@ -250,14 +250,13 @@ function test() {
);
});
expectEvent(EVENTS.RECEIVED_RESPONSE_CONTENT, async () => {
expectEvent(EVENTS.PAYLOAD_READY, async () => {
await waitUntil(() => {
let requestItem = getSortedRequests(store.getState()).get(0);
return requestItem &&
requestItem.transferredSize &&
requestItem.contentSize &&
requestItem.mimeType &&
requestItem.responseContent;
requestItem.mimeType;
});
let requestItem = getSortedRequests(store.getState()).get(0);
@ -269,21 +268,6 @@ function test() {
is(requestItem.mimeType, "text/plain; charset=utf-8",
"The mimeType data has an incorrect value.");
ok(requestItem.responseContent,
"There should be a responseContent data available.");
// eslint-disable-next-line mozilla/no-cpows-in-tests
is(requestItem.responseContent.content.mimeType,
"text/plain; charset=utf-8",
"The responseContent data has an incorrect |content.mimeType| property.");
// eslint-disable-next-line mozilla/no-cpows-in-tests
is(requestItem.responseContent.content.text,
"Hello world!",
"The responseContent data has an incorrect |content.text| property.");
// eslint-disable-next-line mozilla/no-cpows-in-tests
is(requestItem.responseContent.content.size,
12,
"The responseContent data has an incorrect |content.size| property.");
verifyRequestItemTarget(
document,
getDisplayedRequests(store.getState()),

Просмотреть файл

@ -65,30 +65,16 @@ add_task(function* () {
store.dispatch(Actions.selectRequest(null));
yield selectIndexAndWaitForSourceEditor(0);
yield selectIndexAndWaitForSourceEditor(monitor, 0);
// the hls-m3u8 part
testEditorContent(REQUESTS[0]);
yield selectIndexAndWaitForSourceEditor(1);
yield selectIndexAndWaitForSourceEditor(monitor, 1);
// the mpeg-dash part
testEditorContent(REQUESTS[1]);
return teardown(monitor);
function* selectIndexAndWaitForSourceEditor(index) {
let editor = document.querySelector("#response-panel .CodeMirror-code");
if (!editor) {
let waitDOM = waitForDOM(document, "#response-panel .CodeMirror-code");
EventUtils.sendMouseEvent({ type: "mousedown" },
document.querySelectorAll(".request-list-item")[index]);
document.querySelector("#response-tab").click();
yield waitDOM;
} else {
EventUtils.sendMouseEvent({ type: "mousedown" },
document.querySelectorAll(".request-list-item")[index]);
}
}
function testEditorContent([ fmt, textRe ]) {
ok(document.querySelector(".CodeMirror-line").textContent.match(textRe),
"The text shown in the source editor for " + fmt + " is correct.");

Просмотреть файл

@ -1,40 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
/**
* Test that clicking on the file thumbnail opens the response details tab.
*/
add_task(function* () {
let { tab, monitor } = yield initNetMonitor(CONTENT_TYPE_WITHOUT_CACHE_URL);
let { document } = monitor.panelWin;
yield performRequestsAndWait();
let wait = waitForDOM(document, "#response-panel");
let request = document.querySelectorAll(".request-list-item")[5];
let icon = request.querySelector(".requests-list-icon");
info("Clicking thumbnail of the sixth request.");
EventUtils.synthesizeMouseAtCenter(icon, {}, monitor.panelWin);
yield wait;
ok(document.querySelector("#response-tab[aria-selected=true]"),
"Response tab is selected.");
ok(document.querySelector(".response-image-box"),
"Response image preview is shown.");
yield teardown(monitor);
function* performRequestsAndWait() {
let onAllEvents = waitForNetworkEvents(monitor, CONTENT_TYPE_WITHOUT_CACHE_REQUESTS);
yield ContentTask.spawn(tab.linkedBrowser, {}, function* () {
content.wrappedJSObject.performRequests();
});
yield onAllEvents;
}
});

Просмотреть файл

@ -2,9 +2,10 @@
http://creativecommons.org/publicdomain/zero/1.0/ */
/* import-globals-from ../../framework/test/shared-head.js */
/* import-globals-from shared-head.js */
/* exported Toolbox, restartNetMonitor, teardown, waitForExplicitFinish,
verifyRequestItemTarget, waitFor, testFilterButtons, loadCommonFrameScript,
performRequestsInContent, waitForNetworkEvents */
performRequestsInContent, waitForNetworkEvents, selectIndexAndWaitForSourceEditor */
"use strict";
@ -13,7 +14,10 @@ Services.scriptloader.loadSubScript(
"chrome://mochitests/content/browser/devtools/client/framework/test/shared-head.js",
this);
const { EVENTS } = require("devtools/client/netmonitor/src/constants");
Services.scriptloader.loadSubScript(
"chrome://mochitests/content/browser/devtools/client/netmonitor/test/shared-head.js",
this);
const {
getFormattedIPAndPort,
getFormattedTime,
@ -281,6 +285,12 @@ function teardown(monitor) {
return Task.spawn(function* () {
let tab = monitor.toolbox.target.tab;
// Ensure that there is no pending RDP requests related to payload request
// done from FirefoxDataProvider.
info("Wait for completion of all pending RDP requests...");
yield waitForExistingRequests(monitor);
info("All pending requests finished.");
let onDestroyed = monitor.once("destroyed");
yield removeTab(tab);
yield onDestroyed;
@ -306,13 +316,14 @@ function waitForNetworkEvents(monitor, getRequests, postRequests = 0) {
["RECEIVED_RESPONSE_HEADERS", onGenericEvent],
["UPDATING_RESPONSE_COOKIES", onGenericEvent],
["RECEIVED_RESPONSE_COOKIES", onGenericEvent],
["STARTED_RECEIVING_RESPONSE", onGenericEvent],
["UPDATING_RESPONSE_CONTENT", onGenericEvent],
["RECEIVED_RESPONSE_CONTENT", onGenericEvent],
["UPDATING_EVENT_TIMINGS", onGenericEvent],
["RECEIVED_EVENT_TIMINGS", onGenericEvent],
["PAYLOAD_READY", onPayloadReady]
];
let expectedGenericEvents = awaitedEventsToListeners
.filter(([, listener]) => listener == onGenericEvent).length;
let expectedPostEvents = awaitedEventsToListeners
.filter(([, listener]) => listener == onPostEvent).length;
function initProgressForURL(url) {
if (progress[url]) {
@ -365,8 +376,10 @@ function waitForNetworkEvents(monitor, getRequests, postRequests = 0) {
function maybeResolve(event, actor, networkInfo) {
info("> Network events progress: " +
genericEvents + "/" + ((getRequests + postRequests) * 13) + ", " +
postEvents + "/" + (postRequests * 2) + ", " +
"Payload: " + payloadReady + "/" + (getRequests + postRequests) + ", " +
"Generic: " + genericEvents + "/" +
((getRequests + postRequests) * expectedGenericEvents) + ", " +
"Post: " + postEvents + "/" + (postRequests * expectedPostEvents) + ", " +
"got " + event + " for " + actor);
let url = networkInfo.request.url;
@ -375,12 +388,12 @@ function waitForNetworkEvents(monitor, getRequests, postRequests = 0) {
// Uncomment this to get a detailed progress logging (when debugging a test)
// info("> Current state: " + JSON.stringify(progress, null, 2));
// There are 15 updates which need to be fired for a request to be
// considered finished. The "requestPostData" packet isn't fired for
// non-POST requests.
// There are `expectedGenericEvents` updates which need to be fired for a request
// to be considered finished. The "requestPostData" packet isn't fired for non-POST
// requests.
if (payloadReady >= (getRequests + postRequests) &&
genericEvents >= (getRequests + postRequests) * 13 &&
postEvents >= postRequests * 2) {
genericEvents >= (getRequests + postRequests) * expectedGenericEvents &&
postEvents >= postRequests * expectedPostEvents) {
awaitedEventsToListeners.forEach(([e, l]) => panel.off(EVENTS[e], l));
executeSoon(resolve);
}
@ -684,3 +697,25 @@ function waitForContentMessage(name) {
});
});
}
/**
* Select a request and switch to its response panel.
*
* @param {Number} index The request index to be selected
*/
async function selectIndexAndWaitForSourceEditor(monitor, index) {
let document = monitor.panelWin.document;
let onResponseContent = monitor.panelWin.once(EVENTS.RECEIVED_RESPONSE_CONTENT);
// Select the request first, as it may try to fetch whatever is the current request's
// responseContent if we select the ResponseTab first.
EventUtils.sendMouseEvent({ type: "mousedown" },
document.querySelectorAll(".request-list-item")[index]);
// We may already be on the ResponseTab, so only select it if needed.
let editor = document.querySelector("#response-panel .CodeMirror-code");
if (!editor) {
let waitDOM = waitForDOM(document, "#response-panel .CodeMirror-code");
document.querySelector("#response-tab").click();
await waitDOM;
}
await onResponseContent;
}

Просмотреть файл

@ -31,10 +31,16 @@
// Use a count parameter to defeat caching.
let count = 0;
let doRequests = true;
function stopRequests() { // eslint-disable-line no-unused-vars
doRequests = false;
}
(function performRequests() {
get("request_" + (count++), function () {
setTimeout(performRequests, 50);
if (doRequests) {
setTimeout(performRequests, 50);
}
});
})();
</script>

Просмотреть файл

@ -0,0 +1,43 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/* exported EVENTS, waitForExistingRequests */
"use strict";
const { EVENTS } = require("devtools/client/netmonitor/src/constants");
async function waitForExistingRequests(monitor) {
let { store } = monitor.panelWin;
function getRequests() {
return store.getState().requests.requests;
}
function areAllRequestsFullyLoaded() {
let requests = getRequests().valueSeq();
for (let request of requests) {
// Ignore cloned request as we don't lazily fetch data for them
// and have arbitrary number of field set.
if (request.id.includes("-clone")) {
continue;
}
// Do same check than FirefoxDataProvider.isRequestPayloadReady,
// in order to ensure there is no more pending payload requests to be done.
if (!request.requestHeaders || !request.requestCookies ||
!request.eventTimings ||
(!request.securityInfo && !request.fromServiceWorker) ||
((!request.responseHeaders || !request.responseCookies) &&
request.securityState != "broken" &&
(!request.responseContentAvailable || request.status))) {
return false;
}
}
return true;
}
// If there is no request, we are good to go.
if (getRequests().size == 0) {
return;
}
while (!areAllRequestsFullyLoaded()) {
await monitor.panelWin.once(EVENTS.PAYLOAD_READY);
}
}

Просмотреть файл

@ -58,6 +58,7 @@ support-files =
!/devtools/client/inspector/shared/test/head.js
!/devtools/client/inspector/test/head.js
!/devtools/client/inspector/test/shared-head.js
!/devtools/client/netmonitor/test/shared-head.js
!/devtools/client/responsive.html/test/browser/devices.json
!/devtools/client/shared/test/test-actor-registry.js
!/devtools/client/shared/test/test-actor.js

Просмотреть файл

@ -3,9 +3,14 @@
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
/* import-globals-from ../../netmonitor/test/shared-head.js */
// A test to ensure Style Editor doesn't bybass cache when loading style sheet
// contents (bug 978688).
Services.scriptloader.loadSubScript(
"chrome://mochitests/content/browser/devtools/client/netmonitor/test/shared-head.js", this);
const TEST_URL = TEST_BASE_HTTP + "doc_uncached.html";
add_task(function* () {
@ -34,6 +39,8 @@ add_task(function* () {
info("Waiting for the source to be loaded.");
yield styleeditor.UI.editors[0].getSourceEditor();
yield waitForExistingRequests(monitor);
info("Checking Netmonitor contents.");
let items = [];
for (let item of getSortedRequests(store.getState())) {

Просмотреть файл

@ -116,6 +116,9 @@ function NetworkEventMessage({
sendHTTPRequest: () => {},
setPreferences: () => {},
triggerActivity: () => {},
requestData: (requestId, dataType) => {
return serviceContainer.requestData(requestId, dataType);
},
};
// Only render the attachment if the network-event is

Просмотреть файл

@ -96,6 +96,9 @@ NewConsoleOutputWrapper.prototype = {
getLongString: (grip) => {
return hud.proxy.webConsoleClient.getString(grip);
},
requestData(id, type) {
return hud.proxy.networkDataProvider.requestData(id, type);
},
};
// Set `openContextMenu` this way so, `serviceContainer` variable

Просмотреть файл

@ -177,6 +177,13 @@ function enableNetProvider(hud) {
actions,
webConsoleClient: proxy.webConsoleClient
});
// /!\ This is terrible, but it allows ResponsePanel to be able to call
// `dataProvider.requestData` to fetch response content lazily.
// `proxy.networkDataProvider` is put by NewConsoleOutputWrapper on
// `serviceContainer` which allow NetworkEventMessage to expose requestData on
// the fake `connector` object it hands over to ResponsePanel.
proxy.networkDataProvider = dataProvider;
}
let type = action.type;

Просмотреть файл

@ -64,4 +64,6 @@ async function testNetmonitor(toolbox) {
let item = getSortedRequests(store.getState()).get(0);
is(item.method, "GET", "The attached method is correct.");
is(item.url, TEST_PATH, "The attached url is correct.");
await waitForExistingRequests(monitor);
}

Просмотреть файл

@ -49,6 +49,8 @@ add_task(async function task() {
info("network-request-payload-ready received");
await testNetworkMessage(messageNode);
await waitForExistingRequests(monitor);
});
async function testNetworkMessage(messageNode) {

Просмотреть файл

@ -75,4 +75,7 @@ async function testNetmonitorLink(toolbox, hud, url) {
});
ok(true, "The attached url is correct.");
let monitor = toolbox.getCurrentPanel();
await waitForExistingRequests(monitor);
}

Просмотреть файл

@ -3,6 +3,7 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
/* import-globals-from ../../../../framework/test/shared-head.js */
/* import-globals-from ../../../../netmonitor/test/shared-head.js */
/* eslint no-unused-vars: [2, {"vars": "local"}] */
"use strict";
@ -13,6 +14,9 @@ Services.scriptloader.loadSubScript(
"chrome://mochitests/content/browser/devtools/client/framework/test/shared-head.js",
this);
Services.scriptloader.loadSubScript(
"chrome://mochitests/content/browser/devtools/client/netmonitor/test/shared-head.js", this);
var {HUDService} = require("devtools/client/webconsole/hudservice");
var WCUL10n = require("devtools/client/webconsole/webconsole-l10n");
const DOCS_GA_PARAMS = "?utm_source=mozilla" +

Просмотреть файл

@ -5,6 +5,9 @@
"use strict";
Services.scriptloader.loadSubScript(
"chrome://mochitests/content/browser/devtools/client/netmonitor/test/shared-head.js", this);
const TEST_URI = "data:text/html;charset=utf8,Test that the netmonitor " +
"displays requests that have been recorded in the " +
"web console, even if the netmonitor hadn't opened yet.";
@ -76,4 +79,6 @@ function* testNetmonitor(toolbox) {
let item = getSortedRequests(store.getState()).get(0);
is(item.method, "GET", "The attached method is correct.");
is(item.url, TEST_PATH, "The attached url is correct.");
yield waitForExistingRequests(monitor);
}

Просмотреть файл

@ -2,6 +2,8 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
/* import-globals-from ../../netmonitor/test/shared-head.js */
// Tests that network log messages bring up the network panel.
"use strict";
@ -10,6 +12,9 @@ const TEST_NETWORK_REQUEST_URI =
"http://example.com/browser/devtools/client/webconsole/test/" +
"test-network-request.html";
Services.scriptloader.loadSubScript(
"chrome://mochitests/content/browser/devtools/client/netmonitor/test/shared-head.js", this);
add_task(function* () {
let finishedRequest = waitForFinishedRequest(({ request }) => {
return request.url.endsWith("test-network-request.html");
@ -21,9 +26,9 @@ add_task(function* () {
yield hud.ui.openNetworkPanel(request.actor);
let toolbox = gDevTools.getToolbox(hud.target);
is(toolbox.currentToolId, "netmonitor", "Network panel was opened");
let panel = toolbox.getCurrentPanel();
let monitor = toolbox.getCurrentPanel();
let { store, windowRequire } = panel.panelWin;
let { store, windowRequire } = monitor.panelWin;
let { getSelectedRequest } = windowRequire("devtools/client/netmonitor/src/selectors/index");
let selected = getSelectedRequest(store.getState());
@ -31,4 +36,6 @@ add_task(function* () {
"The correct request is selected");
is(selected.url, request.request.url,
"The correct request is definitely selected");
yield waitForExistingRequests(monitor);
});

Просмотреть файл

@ -3,6 +3,8 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
/* import-globals-from ../../netmonitor/test/shared-head.js */
// Tests that network log messages bring up the network panel and select the
// right request even if it was previously filtered off.
@ -13,6 +15,9 @@ const TEST_FILE_URI =
"test-network.html";
const TEST_URI = "data:text/html;charset=utf8,<p>test file URI";
Services.scriptloader.loadSubScript(
"chrome://mochitests/content/browser/devtools/client/netmonitor/test/shared-head.js", this);
var hud;
add_task(function* () {
@ -37,8 +42,8 @@ add_task(function* () {
let toolbox = gDevTools.getToolbox(hud.target);
is(toolbox.currentToolId, "netmonitor", "Network panel was opened");
let panel = toolbox.getCurrentPanel();
let { store, windowRequire } = panel.panelWin;
let monitor = toolbox.getCurrentPanel();
let { store, windowRequire } = monitor.panelWin;
let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
let { getSelectedRequest } = windowRequire("devtools/client/netmonitor/src/selectors/index");
@ -64,6 +69,8 @@ add_task(function* () {
// All tests are done. Shutdown.
HUDService.lastFinishedRequest.callback = null;
htmlRequest = browser = requests = hud = null;
yield waitForExistingRequests(monitor);
});
function testMessages() {

Просмотреть файл

@ -11984,7 +11984,7 @@ class CGDeleteNamedProperty(CGAbstractStaticMethod):
MOZ_ASSERT(js::IsProxy(proxy));
MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy));
JSAutoCompartment ac(cx, proxy);
bool deleteSucceeded;
bool deleteSucceeded = false;
bool found = false;
$*{namedBody}
if (!found || deleteSucceeded) {

Просмотреть файл

@ -147,6 +147,11 @@ MP4Demuxer::Init()
new mp4_demuxer::BufferStream(initData.Ref());
mp4_demuxer::MP4Metadata metadata{bufferstream};
nsresult rv = metadata.Parse();
if (NS_FAILED(rv)) {
return InitPromise::CreateAndReject(
MediaResult(rv, RESULT_DETAIL("Parse MP4 metadata failed")), __func__);
}
auto audioTrackCount = metadata.GetNumberTracks(TrackInfo::kAudioTrack);
if (audioTrackCount.Ref() == mp4_demuxer::MP4Metadata::NumberTracksError()) {

Просмотреть файл

@ -855,6 +855,7 @@ WebRenderBridgeParent::RecvRemovePipelineIdForCompositable(const wr::PipelineId&
wrHost->ClearWrBridge();
mAsyncImageManager->RemoveAsyncImagePipeline(aPipelineId);
mAsyncCompositables.Remove(wr::AsUint64(aPipelineId));
mApi->RemovePipeline(aPipelineId);
return IPC_OK();
}

Просмотреть файл

@ -438,6 +438,12 @@ WebRenderAPI::SetRootPipeline(PipelineId aPipeline)
wr_api_set_root_pipeline(mDocHandle, aPipeline);
}
void
WebRenderAPI::RemovePipeline(PipelineId aPipeline)
{
wr_api_remove_pipeline(mDocHandle, aPipeline);
}
void
WebRenderAPI::UpdateResources(ResourceUpdateQueue& aUpdates)
{

Просмотреть файл

@ -164,6 +164,8 @@ public:
void SetRootPipeline(wr::PipelineId aPipeline);
void RemovePipeline(wr::PipelineId aPipeline);
void UpdateResources(ResourceUpdateQueue& aUpdates);
void UpdatePipelineResources(ResourceUpdateQueue& aUpdates, PipelineId aPipeline, Epoch aEpoch);

Просмотреть файл

@ -891,6 +891,12 @@ pub extern "C" fn wr_api_set_root_pipeline(dh: &mut DocumentHandle,
dh.api.set_root_pipeline(dh.document_id, pipeline_id);
}
#[no_mangle]
pub extern "C" fn wr_api_remove_pipeline(dh: &mut DocumentHandle,
pipeline_id: WrPipelineId) {
dh.api.remove_pipeline(dh.document_id, pipeline_id);
}
#[no_mangle]
pub extern "C" fn wr_api_set_window_parameters(dh: &mut DocumentHandle,
width: i32,

Просмотреть файл

@ -1005,6 +1005,11 @@ WR_INLINE
WrIdNamespace wr_api_get_namespace(DocumentHandle *aDh)
WR_FUNC;
WR_INLINE
void wr_api_remove_pipeline(DocumentHandle *aDh,
WrPipelineId aPipelineId)
WR_FUNC;
WR_INLINE
void wr_api_send_external_event(DocumentHandle *aDh,
size_t aEvt)

Просмотреть файл

@ -4088,7 +4088,6 @@ nsFrame::HandlePress(nsPresContext* aPresContext,
// the mouse on the nearest scrollable frame. If there isn't a scrollable
// frame, or something else is already capturing the mouse, there's no
// reason to capture.
bool hasCapturedContent = false;
if (!nsIPresShell::GetCapturingContent()) {
nsIScrollableFrame* scrollFrame =
nsLayoutUtils::GetNearestScrollableFrame(this,
@ -4098,7 +4097,6 @@ nsFrame::HandlePress(nsPresContext* aPresContext,
nsIFrame* capturingFrame = do_QueryFrame(scrollFrame);
nsIPresShell::SetCapturingContent(capturingFrame->GetContent(),
CAPTURE_IGNOREALLOWED);
hasCapturedContent = true;
}
}
@ -4136,24 +4134,6 @@ nsFrame::HandlePress(nsPresContext* aPresContext,
if (!offsets.content)
return NS_ERROR_FAILURE;
// On touchables devices, touch the screen is usually a pan action,
// so let's reposition the caret if needed but do not select text
// if the touch did not happen over an editable element. Otherwise,
// let the user move the caret by tapping and dragging.
if (!offsets.content->IsEditable() &&
Preferences::GetBool("browser.ignoreNativeFrameTextSelection", false)) {
// On touchables devices, mouse events are generated if the gesture is a tap.
// Such events are never going to generate a drag action, so let's release
// captured content if any.
if (hasCapturedContent) {
nsIPresShell::SetCapturingContent(nullptr, 0);
}
return fc->HandleClick(offsets.content, offsets.StartOffset(),
offsets.EndOffset(), false, false,
offsets.associate);
}
// Let Ctrl/Cmd+mouse down do table selection instead of drag initiation
nsCOMPtr<nsIContent>parentContent;
int32_t contentOffset;

Просмотреть файл

@ -6,87 +6,19 @@
#include "mp4_demuxer/AnnexB.h"
#include "mp4_demuxer/BufferReader.h"
#include "mp4_demuxer/DecoderData.h"
#include <media/stagefright/foundation/ABitReader.h>
#include "media/stagefright/MetaData.h"
#include "media/stagefright/MediaDefs.h"
#include "media/stagefright/Utils.h"
#include "mozilla/ArrayUtils.h"
#include "mozilla/EndianUtils.h"
#include "include/ESDS.h"
#include "VideoUtils.h"
// OpusDecoder header is really needed only by MP4 in rust
#include "OpusDecoder.h"
#include "mp4parse.h"
using namespace stagefright;
using mozilla::media::TimeUnit;
namespace mp4_demuxer
{
static int32_t
FindInt32(const MetaData* mMetaData, uint32_t mKey)
{
int32_t value;
if (!mMetaData->findInt32(mKey, &value))
return 0;
return value;
}
static int64_t
FindInt64(const MetaData* mMetaData, uint32_t mKey)
{
int64_t value;
if (!mMetaData->findInt64(mKey, &value))
return 0;
return value;
}
template <typename T, size_t N>
static bool
FindData(const MetaData* aMetaData, uint32_t aKey, mozilla::Vector<T, N>* aDest)
{
const void* data;
size_t size;
uint32_t type;
aDest->clear();
// There's no point in checking that the type matches anything because it
// isn't set consistently in the MPEG4Extractor.
if (!aMetaData->findData(aKey, &type, &data, &size) || size % sizeof(T)) {
return false;
}
aDest->append(reinterpret_cast<const T*>(data), size / sizeof(T));
return true;
}
template <typename T>
static bool
FindData(const MetaData* aMetaData, uint32_t aKey, nsTArray<T>* aDest)
{
const void* data;
size_t size;
uint32_t type;
aDest->Clear();
// There's no point in checking that the type matches anything because it
// isn't set consistently in the MPEG4Extractor.
if (!aMetaData->findData(aKey, &type, &data, &size) || size % sizeof(T)) {
return false;
}
aDest->AppendElements(reinterpret_cast<const T*>(data), size / sizeof(T));
return true;
}
static bool
FindData(const MetaData* aMetaData, uint32_t aKey, mozilla::MediaByteBuffer* aDest)
{
return FindData(aMetaData, aKey, static_cast<nsTArray<uint8_t>*>(aDest));
}
mozilla::Result<mozilla::Ok, nsresult>
CryptoFile::DoUpdate(const uint8_t* aData, size_t aLength)
{
@ -110,75 +42,15 @@ CryptoFile::DoUpdate(const uint8_t* aData, size_t aLength)
return mozilla::Ok();
}
static void
UpdateTrackInfo(mozilla::TrackInfo& aConfig,
const MetaData* aMetaData,
const char* aMimeType)
{
mozilla::CryptoTrack& crypto = aConfig.mCrypto;
aConfig.mMimeType = aMimeType;
aConfig.mDuration = TimeUnit::FromMicroseconds(
FindInt64(aMetaData, kKeyDuration));
aConfig.mMediaTime = TimeUnit::FromMicroseconds(
FindInt64(aMetaData, kKeyMediaTime));
aConfig.mTrackId = FindInt32(aMetaData, kKeyTrackID);
aConfig.mCrypto.mValid = aMetaData->findInt32(kKeyCryptoMode, &crypto.mMode) &&
aMetaData->findInt32(kKeyCryptoDefaultIVSize, &crypto.mIVSize) &&
FindData(aMetaData, kKeyCryptoKey, &crypto.mKeyId);
}
void
MP4AudioInfo::Update(const MetaData* aMetaData,
const char* aMimeType)
{
UpdateTrackInfo(*this, aMetaData, aMimeType);
mChannels = FindInt32(aMetaData, kKeyChannelCount);
mBitDepth = FindInt32(aMetaData, kKeySampleSize);
mRate = FindInt32(aMetaData, kKeySampleRate);
mProfile = FindInt32(aMetaData, kKeyAACProfile);
if (FindData(aMetaData, kKeyESDS, mExtraData)) {
ESDS esds(mExtraData->Elements(), mExtraData->Length());
const void* data;
size_t size;
if (esds.getCodecSpecificInfo(&data, &size) == OK) {
const uint8_t* cdata = reinterpret_cast<const uint8_t*>(data);
mCodecSpecificConfig->AppendElements(cdata, size);
if (size > 1) {
ABitReader br(cdata, size);
mExtendedProfile = br.getBits(5);
if (mExtendedProfile == 31) { // AAC-ELD => additional 6 bits
mExtendedProfile = 32 + br.getBits(6);
}
}
}
}
}
bool
MP4AudioInfo::IsValid() const
{
return mChannels > 0 && mRate > 0 &&
// Accept any mime type here, but if it's aac, validate the profile.
(!mMimeType.Equals(MEDIA_MIMETYPE_AUDIO_AAC) ||
(!mMimeType.EqualsLiteral("audio/mp4a-latm") ||
mProfile > 0 || mExtendedProfile > 0);
}
void
MP4VideoInfo::Update(const MetaData* aMetaData, const char* aMimeType)
{
UpdateTrackInfo(*this, aMetaData, aMimeType);
mDisplay.width = FindInt32(aMetaData, kKeyDisplayWidth);
mDisplay.height = FindInt32(aMetaData, kKeyDisplayHeight);
mImage.width = FindInt32(aMetaData, kKeyWidth);
mImage.height = FindInt32(aMetaData, kKeyHeight);
mRotation = VideoInfo::ToSupportedRotation(FindInt32(aMetaData, kKeyRotation));
FindData(aMetaData, kKeyAVCC, mExtraData);
}
static void
UpdateTrackProtectedInfo(mozilla::TrackInfo& aConfig,
const mp4parse_sinf_info& aSinf)

Просмотреть файл

@ -10,7 +10,6 @@
#include "mp4_demuxer/BufferReader.h"
#include "mp4_demuxer/ByteWriter.h"
#include "mp4_demuxer/H264.h"
#include <media/stagefright/foundation/ABitReader.h>
#include <limits>
#include <cmath>

Просмотреть файл

@ -13,7 +13,6 @@
#include <algorithm>
#include <limits>
using namespace stagefright;
using namespace mozilla;
using namespace mozilla::media;

Просмотреть файл

@ -2,11 +2,6 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "include/MPEG4Extractor.h"
#include "media/stagefright/DataSource.h"
#include "media/stagefright/MediaDefs.h"
#include "media/stagefright/MediaSource.h"
#include "media/stagefright/MetaData.h"
#include "mozilla/Assertions.h"
#include "mozilla/CheckedInt.h"
#include "mozilla/EndianUtils.h"
@ -25,10 +20,6 @@
#include <stdint.h>
#include <vector>
struct FreeMP4Parser { void operator()(mp4parse_parser* aPtr) { mp4parse_free(aPtr); } };
using namespace stagefright;
using mozilla::media::TimeUnit;
namespace mp4_demuxer
@ -36,158 +27,6 @@ namespace mp4_demuxer
LazyLogModule gMP4MetadataLog("MP4Metadata");
class DataSourceAdapter : public DataSource
{
public:
explicit DataSourceAdapter(Stream* aSource) : mSource(aSource) {}
~DataSourceAdapter() {}
virtual status_t initCheck() const { return NO_ERROR; }
virtual ssize_t readAt(off64_t offset, void* data, size_t size)
{
MOZ_ASSERT(((ssize_t)size) >= 0);
size_t bytesRead;
if (!mSource->ReadAt(offset, data, size, &bytesRead))
return ERROR_IO;
if (bytesRead == 0)
return ERROR_END_OF_STREAM;
MOZ_ASSERT(((ssize_t)bytesRead) > 0);
return bytesRead;
}
virtual status_t getSize(off64_t* size)
{
if (!mSource->Length(size))
return ERROR_UNSUPPORTED;
return NO_ERROR;
}
virtual uint32_t flags() { return kWantsPrefetching | kIsHTTPBasedSource; }
virtual status_t reconnectAtOffset(off64_t offset) { return NO_ERROR; }
private:
RefPtr<Stream> mSource;
};
class MP4MetadataStagefright
{
public:
explicit MP4MetadataStagefright(Stream* aSource);
~MP4MetadataStagefright();
static MP4Metadata::ResultAndByteBuffer Metadata(Stream* aSource);
MP4Metadata::ResultAndTrackCount
GetNumberTracks(mozilla::TrackInfo::TrackType aType) const;
MP4Metadata::ResultAndTrackInfo GetTrackInfo(
mozilla::TrackInfo::TrackType aType, size_t aTrackNumber) const;
bool CanSeek() const;
MP4Metadata::ResultAndCryptoFile Crypto() const;
MediaResult ReadTrackIndex(FallibleTArray<Index::Indice>& aDest, mozilla::TrackID aTrackID);
private:
int32_t GetTrackNumber(mozilla::TrackID aTrackID);
void UpdateCrypto(const stagefright::MetaData* aMetaData);
mozilla::UniquePtr<mozilla::TrackInfo> CheckTrack(const char* aMimeType,
stagefright::MetaData* aMetaData,
int32_t aIndex) const;
CryptoFile mCrypto;
RefPtr<Stream> mSource;
sp<MediaExtractor> mMetadataExtractor;
bool mCanSeek;
};
// Wrap an mp4_demuxer::Stream to remember the read offset.
class RustStreamAdaptor {
public:
explicit RustStreamAdaptor(Stream* aSource)
: mSource(aSource)
, mOffset(0)
{
}
~RustStreamAdaptor() {}
bool Read(uint8_t* buffer, uintptr_t size, size_t* bytes_read);
private:
Stream* mSource;
CheckedInt<size_t> mOffset;
};
class MP4MetadataRust
{
public:
explicit MP4MetadataRust(Stream* aSource);
~MP4MetadataRust();
static MP4Metadata::ResultAndByteBuffer Metadata(Stream* aSource);
MP4Metadata::ResultAndTrackCount
GetNumberTracks(mozilla::TrackInfo::TrackType aType) const;
MP4Metadata::ResultAndTrackInfo GetTrackInfo(
mozilla::TrackInfo::TrackType aType, size_t aTrackNumber) const;
bool CanSeek() const;
MP4Metadata::ResultAndCryptoFile Crypto() const;
MediaResult ReadTrackIndice(mp4parse_byte_data* aIndices, mozilla::TrackID aTrackID);
bool Init();
private:
void UpdateCrypto();
Maybe<uint32_t> TrackTypeToGlobalTrackIndex(mozilla::TrackInfo::TrackType aType, size_t aTrackNumber) const;
CryptoFile mCrypto;
RefPtr<Stream> mSource;
RustStreamAdaptor mRustSource;
mozilla::UniquePtr<mp4parse_parser, FreeMP4Parser> mRustParser;
};
class IndiceWrapperStagefright : public IndiceWrapper {
public:
size_t Length() const override;
bool GetIndice(size_t aIndex, Index::Indice& aIndice) const override;
explicit IndiceWrapperStagefright(FallibleTArray<Index::Indice>& aIndice);
protected:
FallibleTArray<Index::Indice> mIndice;
};
IndiceWrapperStagefright::IndiceWrapperStagefright(FallibleTArray<Index::Indice>& aIndice)
{
mIndice.SwapElements(aIndice);
}
size_t
IndiceWrapperStagefright::Length() const
{
return mIndice.Length();
}
bool
IndiceWrapperStagefright::GetIndice(size_t aIndex, Index::Indice& aIndice) const
{
if (aIndex >= mIndice.Length()) {
MOZ_LOG(gMP4MetadataLog, LogLevel::Error, ("Index overflow in indice"));
return false;
}
aIndice = mIndice[aIndex];
return true;
}
// the owner of mIndice is rust mp4 paser, so lifetime of this class
// SHOULD NOT longer than rust parser.
class IndiceWrapperRust : public IndiceWrapper
@ -234,30 +73,6 @@ IndiceWrapperRust::GetIndice(size_t aIndex, Index::Indice& aIndice) const
return true;
}
MP4Metadata::MP4Metadata(Stream* aSource)
: mStagefright(MakeUnique<MP4MetadataStagefright>(aSource))
, mRust(MakeUnique<MP4MetadataRust>(aSource))
, mReportedAudioTrackTelemetry(false)
, mReportedVideoTrackTelemetry(false)
{
mDisableRust = !MediaPrefs::EnableRustMP4Parser() && !MediaPrefs::RustTestMode();
if (mDisableRust) {
return;
}
// Fallback to stagefright if it fails.
mDisableRust = !mRust->Init();
}
MP4Metadata::~MP4Metadata()
{
}
/*static*/ MP4Metadata::ResultAndByteBuffer
MP4Metadata::Metadata(Stream* aSource)
{
return MP4MetadataStagefright::Metadata(aSource);
}
static const char *
TrackTypeToString(mozilla::TrackInfo::TrackType aType)
{
@ -271,449 +86,8 @@ TrackTypeToString(mozilla::TrackInfo::TrackType aType)
}
}
MP4Metadata::ResultAndTrackCount
MP4Metadata::GetNumberTracks(mozilla::TrackInfo::TrackType aType) const
{
MP4Metadata::ResultAndTrackCount numTracks =
mStagefright->GetNumberTracks(aType);
MP4Metadata::ResultAndTrackCount numTracksRust =
mRust->GetNumberTracks(aType);
MOZ_LOG(gMP4MetadataLog, LogLevel::Info, ("%s tracks found: stagefright=(%s)%u rust=(%s)%u",
TrackTypeToString(aType),
numTracks.Result().Description().get(),
numTracks.Ref(),
numTracksRust.Result().Description().get(),
numTracksRust.Ref()));
// Consider '0' and 'error' the same for comparison purposes.
// (Mostly because Stagefright never returns errors, but Rust may.)
bool numTracksMatch =
(numTracks.Ref() != NumberTracksError() ? numTracks.Ref() : 0) ==
(numTracksRust.Ref() != NumberTracksError() ? numTracksRust.Ref() : 0);
if (aType == mozilla::TrackInfo::kAudioTrack && !mReportedAudioTrackTelemetry) {
Telemetry::Accumulate(Telemetry::MEDIA_RUST_MP4PARSE_TRACK_MATCH_AUDIO,
numTracksMatch);
mReportedAudioTrackTelemetry = true;
} else if (aType == mozilla::TrackInfo::kVideoTrack && !mReportedVideoTrackTelemetry) {
Telemetry::Accumulate(Telemetry::MEDIA_RUST_MP4PARSE_TRACK_MATCH_VIDEO,
numTracksMatch);
mReportedVideoTrackTelemetry = true;
}
if (!numTracksMatch && MediaPrefs::MediaWarningsAsErrorsStageFrightVsRust()) {
return {MediaResult(NS_ERROR_DOM_MEDIA_METADATA_ERR,
RESULT_DETAIL("Different numbers of tracks: "
"Stagefright=%u (%s) Rust=%u (%s)",
numTracks.Ref(),
numTracks.Result().Description().get(),
numTracksRust.Ref(),
numTracksRust.Result().Description().get())),
NumberTracksError()};
}
return mDisableRust ? numTracks : numTracksRust;
}
static const char*
GetDifferentField(const mozilla::TrackInfo& info,
const mozilla::TrackInfo& infoRust)
{
if (infoRust.mId != info.mId) { return "Id"; }
if (infoRust.mKind != info.mKind) { return "Kind"; }
if (infoRust.mLabel != info.mLabel) { return "Label"; }
if (infoRust.mLanguage != info.mLanguage) { return "Language"; }
if (infoRust.mEnabled != info.mEnabled) { return "Enabled"; }
if (infoRust.mTrackId != info.mTrackId) { return "TrackId"; }
if (infoRust.mMimeType != info.mMimeType) { return "MimeType"; }
if (infoRust.mDuration != info.mDuration) { return "Duration"; }
if (infoRust.mMediaTime != info.mMediaTime) { return "MediaTime"; }
if (infoRust.mCrypto.mValid != info.mCrypto.mValid) { return "Crypto-Valid"; }
if (infoRust.mCrypto.mMode != info.mCrypto.mMode) { return "Crypto-Mode"; }
if (infoRust.mCrypto.mIVSize != info.mCrypto.mIVSize) { return "Crypto-IVSize"; }
if (infoRust.mCrypto.mKeyId != info.mCrypto.mKeyId) { return "Crypto-KeyId"; }
switch (info.GetType()) {
case mozilla::TrackInfo::kAudioTrack: {
const AudioInfo *audioRust = infoRust.GetAsAudioInfo();
const AudioInfo *audio = info.GetAsAudioInfo();
if (audioRust->mRate != audio->mRate) { return "Rate"; }
if (audioRust->mChannels != audio->mChannels) { return "Channels"; }
if (audioRust->mBitDepth != audio->mBitDepth) { return "BitDepth"; }
if (audioRust->mProfile != audio->mProfile) { return "Profile"; }
if (audioRust->mExtendedProfile != audio->mExtendedProfile) { return "ExtendedProfile"; }
break;
}
case mozilla::TrackInfo::kVideoTrack: {
const VideoInfo *videoRust = infoRust.GetAsVideoInfo();
const VideoInfo *video = info.GetAsVideoInfo();
if (videoRust->mDisplay != video->mDisplay) { return "Display"; }
if (videoRust->mImage != video->mImage) { return "Image"; }
if (*videoRust->mExtraData != *video->mExtraData) { return "ExtraData"; }
// mCodecSpecificConfig is for video/mp4-es, not video/avc. Since video/mp4-es
// is supported on b2g only, it could be removed from TrackInfo.
if (*videoRust->mCodecSpecificConfig != *video->mCodecSpecificConfig) { return "CodecSpecificConfig"; }
break;
}
default:
break;
}
return nullptr;
}
MP4Metadata::ResultAndTrackInfo
MP4Metadata::GetTrackInfo(mozilla::TrackInfo::TrackType aType,
size_t aTrackNumber) const
{
MP4Metadata::ResultAndTrackInfo info =
mStagefright->GetTrackInfo(aType, aTrackNumber);
MP4Metadata::ResultAndTrackInfo infoRust =
mRust->GetTrackInfo(aType, aTrackNumber);
if (info.Ref() && infoRust.Ref() && MediaPrefs::MediaWarningsAsErrorsStageFrightVsRust()) {
const char* diff = GetDifferentField(*info.Ref(), *infoRust.Ref());
if (diff) {
return {MediaResult(NS_ERROR_DOM_MEDIA_METADATA_ERR,
RESULT_DETAIL("Different field '%s' between "
"Stagefright (%s) and Rust (%s)",
diff,
info.Result().Description().get(),
infoRust.Result().Description().get())),
MediaPrefs::MediaWarningsAsErrorsStageFrightVsRust()
? mozilla::UniquePtr<mozilla::TrackInfo>(nullptr)
: mDisableRust ? Move(info.Ref()) : Move(infoRust.Ref())};
}
}
return mDisableRust ? Move(info) : Move(infoRust);
}
bool
MP4Metadata::CanSeek() const
{
return mStagefright->CanSeek();
}
MP4Metadata::ResultAndCryptoFile
MP4Metadata::Crypto() const
{
MP4Metadata::ResultAndCryptoFile crypto = mStagefright->Crypto();
MP4Metadata::ResultAndCryptoFile rustCrypto = mRust->Crypto();
if (MediaPrefs::MediaWarningsAsErrorsStageFrightVsRust()) {
if (rustCrypto.Ref()->pssh != crypto.Ref()->pssh) {
return {MediaResult(
NS_ERROR_DOM_MEDIA_METADATA_ERR,
RESULT_DETAIL("Mismatch between Stagefright (%s) and Rust (%s) crypto file",
crypto.Result().Description().get(),
rustCrypto.Result().Description().get())),
mDisableRust ? crypto.Ref() : rustCrypto.Ref()};
}
}
return mDisableRust ? crypto : rustCrypto;
}
MP4Metadata::ResultAndIndice
MP4Metadata::GetTrackIndice(mozilla::TrackID aTrackID)
{
FallibleTArray<Index::Indice> indiceSF;
if (mDisableRust || MediaPrefs::MediaWarningsAsErrorsStageFrightVsRust()) {
MediaResult rv = mStagefright->ReadTrackIndex(indiceSF, aTrackID);
if (NS_FAILED(rv)) {
return {Move(rv), nullptr};
}
}
mp4parse_byte_data indiceRust = {};
if (!mDisableRust || MediaPrefs::MediaWarningsAsErrorsStageFrightVsRust()) {
MediaResult rvRust = mRust->ReadTrackIndice(&indiceRust, aTrackID);
if (NS_FAILED(rvRust)) {
return {Move(rvRust), nullptr};
}
}
if (MediaPrefs::MediaWarningsAsErrorsStageFrightVsRust()) {
MOZ_DIAGNOSTIC_ASSERT(indiceRust.length == indiceSF.Length());
for (uint32_t i = 0; i < indiceRust.length; i++) {
MOZ_DIAGNOSTIC_ASSERT(indiceRust.indices[i].start_offset == indiceSF[i].start_offset);
MOZ_DIAGNOSTIC_ASSERT(indiceRust.indices[i].end_offset == indiceSF[i].end_offset);
MOZ_DIAGNOSTIC_ASSERT(llabs(indiceRust.indices[i].start_composition - int64_t(indiceSF[i].start_composition)) <= 1);
MOZ_DIAGNOSTIC_ASSERT(llabs(indiceRust.indices[i].end_composition - int64_t(indiceSF[i].end_composition)) <= 1);
MOZ_DIAGNOSTIC_ASSERT(llabs(indiceRust.indices[i].start_decode - int64_t(indiceSF[i].start_decode)) <= 1);
MOZ_DIAGNOSTIC_ASSERT(indiceRust.indices[i].sync == indiceSF[i].sync);
}
}
UniquePtr<IndiceWrapper> indice;
if (mDisableRust) {
indice = mozilla::MakeUnique<IndiceWrapperStagefright>(indiceSF);
} else {
indice = mozilla::MakeUnique<IndiceWrapperRust>(indiceRust);
}
return {NS_OK, Move(indice)};
}
static inline MediaResult
ConvertIndex(FallibleTArray<Index::Indice>& aDest,
const nsTArray<stagefright::MediaSource::Indice>& aIndex,
int64_t aMediaTime)
{
if (!aDest.SetCapacity(aIndex.Length(), mozilla::fallible)) {
return MediaResult{NS_ERROR_OUT_OF_MEMORY,
RESULT_DETAIL("Could not resize to %zu indices",
aIndex.Length())};
}
for (size_t i = 0; i < aIndex.Length(); i++) {
Index::Indice indice;
const stagefright::MediaSource::Indice& s_indice = aIndex[i];
indice.start_offset = s_indice.start_offset;
indice.end_offset = s_indice.end_offset;
indice.start_composition = s_indice.start_composition - aMediaTime;
indice.end_composition = s_indice.end_composition - aMediaTime;
indice.start_decode = s_indice.start_decode;
indice.sync = s_indice.sync;
// FIXME: Make this infallible after bug 968520 is done.
MOZ_ALWAYS_TRUE(aDest.AppendElement(indice, mozilla::fallible));
MOZ_LOG(gMP4MetadataLog, LogLevel::Debug,
("s_o: %" PRIu64 ", e_o: %" PRIu64 ", s_c: %" PRIu64 ", e_c: %" PRIu64 ", s_d: %" PRIu64 ", sync: %d\n",
indice.start_offset, indice.end_offset, indice.start_composition,
indice.end_composition, indice.start_decode, indice.sync));
}
return NS_OK;
}
MP4MetadataStagefright::MP4MetadataStagefright(Stream* aSource)
: mSource(aSource)
, mMetadataExtractor(new MPEG4Extractor(new DataSourceAdapter(mSource)))
, mCanSeek(mMetadataExtractor->flags() & MediaExtractor::CAN_SEEK)
{
sp<MetaData> metaData = mMetadataExtractor->getMetaData();
if (metaData.get()) {
UpdateCrypto(metaData.get());
}
}
MP4MetadataStagefright::~MP4MetadataStagefright()
{
}
MP4Metadata::ResultAndTrackCount
MP4MetadataStagefright::GetNumberTracks(mozilla::TrackInfo::TrackType aType) const
{
size_t tracks = mMetadataExtractor->countTracks();
uint32_t total = 0;
for (size_t i = 0; i < tracks; i++) {
sp<MetaData> metaData = mMetadataExtractor->getTrackMetaData(i);
const char* mimeType;
if (metaData == nullptr || !metaData->findCString(kKeyMIMEType, &mimeType)) {
continue;
}
switch (aType) {
case mozilla::TrackInfo::kAudioTrack:
if (!strncmp(mimeType, "audio/", 6) &&
CheckTrack(mimeType, metaData.get(), i)) {
total++;
}
break;
case mozilla::TrackInfo::kVideoTrack:
if (!strncmp(mimeType, "video/", 6) &&
CheckTrack(mimeType, metaData.get(), i)) {
total++;
}
break;
default:
break;
}
}
return {NS_OK, total};
}
MP4Metadata::ResultAndTrackInfo
MP4MetadataStagefright::GetTrackInfo(mozilla::TrackInfo::TrackType aType,
size_t aTrackNumber) const
{
size_t tracks = mMetadataExtractor->countTracks();
if (!tracks) {
return {MediaResult(NS_ERROR_DOM_MEDIA_METADATA_ERR,
RESULT_DETAIL("No %s tracks",
TrackTypeToStr(aType))),
nullptr};
}
int32_t index = -1;
const char* mimeType;
sp<MetaData> metaData;
size_t i = 0;
while (i < tracks) {
metaData = mMetadataExtractor->getTrackMetaData(i);
if (metaData == nullptr || !metaData->findCString(kKeyMIMEType, &mimeType)) {
continue;
}
switch (aType) {
case mozilla::TrackInfo::kAudioTrack:
if (!strncmp(mimeType, "audio/", 6) &&
CheckTrack(mimeType, metaData.get(), i)) {
index++;
}
break;
case mozilla::TrackInfo::kVideoTrack:
if (!strncmp(mimeType, "video/", 6) &&
CheckTrack(mimeType, metaData.get(), i)) {
index++;
}
break;
default:
break;
}
if (index == aTrackNumber) {
break;
}
i++;
}
if (index < 0) {
return {MediaResult(NS_ERROR_DOM_MEDIA_METADATA_ERR,
RESULT_DETAIL("Cannot access %s track #%zu",
TrackTypeToStr(aType),
aTrackNumber)),
nullptr};
}
UniquePtr<mozilla::TrackInfo> e = CheckTrack(mimeType, metaData.get(), index);
if (e) {
metaData = mMetadataExtractor->getMetaData();
int64_t movieDuration;
if (!e->mDuration.IsPositive() &&
metaData->findInt64(kKeyMovieDuration, &movieDuration)) {
// No duration in track, use movie extend header box one.
e->mDuration = TimeUnit::FromMicroseconds(movieDuration);
}
}
return {NS_OK, Move(e)};
}
mozilla::UniquePtr<mozilla::TrackInfo>
MP4MetadataStagefright::CheckTrack(const char* aMimeType,
stagefright::MetaData* aMetaData,
int32_t aIndex) const
{
sp<MediaSource> track = mMetadataExtractor->getTrack(aIndex);
if (!track.get()) {
return nullptr;
}
UniquePtr<mozilla::TrackInfo> e;
if (!strncmp(aMimeType, "audio/", 6)) {
auto info = mozilla::MakeUnique<MP4AudioInfo>();
info->Update(aMetaData, aMimeType);
e = Move(info);
} else if (!strncmp(aMimeType, "video/", 6)) {
auto info = mozilla::MakeUnique<MP4VideoInfo>();
info->Update(aMetaData, aMimeType);
e = Move(info);
}
if (e && e->IsValid()) {
return e;
}
return nullptr;
}
bool
MP4MetadataStagefright::CanSeek() const
{
return mCanSeek;
}
MP4Metadata::ResultAndCryptoFile
MP4MetadataStagefright::Crypto() const
{
return {NS_OK, &mCrypto};
}
void
MP4MetadataStagefright::UpdateCrypto(const MetaData* aMetaData)
{
const void* data;
size_t size;
uint32_t type;
// There's no point in checking that the type matches anything because it
// isn't set consistently in the MPEG4Extractor.
if (!aMetaData->findData(kKeyPssh, &type, &data, &size)) {
return;
}
mCrypto.Update(reinterpret_cast<const uint8_t*>(data), size);
}
MediaResult
MP4MetadataStagefright::ReadTrackIndex(FallibleTArray<Index::Indice>& aDest, mozilla::TrackID aTrackID)
{
size_t numTracks = mMetadataExtractor->countTracks();
int32_t trackNumber = GetTrackNumber(aTrackID);
if (trackNumber < 0) {
return MediaResult(NS_ERROR_DOM_MEDIA_METADATA_ERR,
RESULT_DETAIL("Cannot find track id %d",
int(aTrackID)));
}
sp<MediaSource> track = mMetadataExtractor->getTrack(trackNumber);
if (!track.get()) {
return MediaResult(NS_ERROR_DOM_MEDIA_METADATA_ERR,
RESULT_DETAIL("Cannot access track id %d",
int(aTrackID)));
}
sp<MetaData> metadata = mMetadataExtractor->getTrackMetaData(trackNumber);
int64_t mediaTime;
if (!metadata->findInt64(kKeyMediaTime, &mediaTime)) {
mediaTime = 0;
}
return ConvertIndex(aDest, track->exportIndex(), mediaTime);
}
int32_t
MP4MetadataStagefright::GetTrackNumber(mozilla::TrackID aTrackID)
{
size_t numTracks = mMetadataExtractor->countTracks();
for (size_t i = 0; i < numTracks; i++) {
sp<MetaData> metaData = mMetadataExtractor->getTrackMetaData(i);
if (!metaData.get()) {
continue;
}
int32_t value;
if (metaData->findInt32(kKeyTrackID, &value) && value == aTrackID) {
return i;
}
}
return -1;
}
/*static*/ MP4Metadata::ResultAndByteBuffer
MP4MetadataStagefright::Metadata(Stream* aSource)
{
auto parser = mozilla::MakeUnique<MoofParser>(aSource, 0, false);
RefPtr<mozilla::MediaByteBuffer> buffer = parser->Metadata();
if (!buffer) {
return {MediaResult(NS_ERROR_DOM_MEDIA_METADATA_ERR,
RESULT_DETAIL("Cannot parse metadata")),
nullptr};
}
return {NS_OK, Move(buffer)};
}
bool
RustStreamAdaptor::Read(uint8_t* buffer, uintptr_t size, size_t* bytes_read)
StreamAdaptor::Read(uint8_t* buffer, uintptr_t size, size_t* bytes_read)
{
if (!mOffset.isValid()) {
MOZ_LOG(gMP4MetadataLog, LogLevel::Error, ("Overflow in source stream offset"));
@ -733,7 +107,7 @@ read_source(uint8_t* buffer, uintptr_t size, void* userdata)
MOZ_ASSERT(buffer);
MOZ_ASSERT(userdata);
auto source = reinterpret_cast<RustStreamAdaptor*>(userdata);
auto source = reinterpret_cast<StreamAdaptor*>(userdata);
size_t bytes_read = 0;
bool rv = source->Read(buffer, size, &bytes_read);
if (!rv) {
@ -743,48 +117,43 @@ read_source(uint8_t* buffer, uintptr_t size, void* userdata)
return bytes_read;
}
MP4MetadataRust::MP4MetadataRust(Stream* aSource)
MP4Metadata::MP4Metadata(Stream* aSource)
: mSource(aSource)
, mRustSource(aSource)
, mSourceAdaptor(aSource)
{
}
MP4MetadataRust::~MP4MetadataRust()
{
}
bool
MP4MetadataRust::Init()
{
mp4parse_io io = { read_source, &mRustSource };
mRustParser.reset(mp4parse_new(&io));
MOZ_ASSERT(mRustParser);
mp4parse_io io = { read_source, &mSourceAdaptor };
mParser.reset(mp4parse_new(&io));
MOZ_ASSERT(mParser);
if (MOZ_LOG_TEST(gMP4MetadataLog, LogLevel::Debug)) {
mp4parse_log(true);
}
}
mp4parse_status rv = mp4parse_read(mRustParser.get());
MOZ_LOG(gMP4MetadataLog, LogLevel::Debug, ("rust parser returned %d\n", rv));
Telemetry::Accumulate(Telemetry::MEDIA_RUST_MP4PARSE_SUCCESS,
rv == mp4parse_status_OK);
if (rv != mp4parse_status_OK && rv != mp4parse_status_OOM) {
MOZ_LOG(gMP4MetadataLog, LogLevel::Info, ("Rust mp4 parser fails to parse this stream."));
MOZ_ASSERT(rv > 0);
Telemetry::Accumulate(Telemetry::MEDIA_RUST_MP4PARSE_ERROR_CODE, rv);
return false;
MP4Metadata::~MP4Metadata()
{
}
nsresult
MP4Metadata::Parse()
{
mp4parse_status rv = mp4parse_read(mParser.get());
if (rv != mp4parse_status_OK) {
MOZ_LOG(gMP4MetadataLog, LogLevel::Debug, ("Parse failed, return code %d\n", rv));
return rv == mp4parse_status_OOM ? NS_ERROR_OUT_OF_MEMORY
: NS_ERROR_DOM_MEDIA_METADATA_ERR;
}
UpdateCrypto();
return true;
return NS_OK;
}
void
MP4MetadataRust::UpdateCrypto()
MP4Metadata::UpdateCrypto()
{
mp4parse_pssh_info info = {};
if (mp4parse_get_pssh_info(mRustParser.get(), &info) != mp4parse_status_OK) {
if (mp4parse_get_pssh_info(mParser.get(), &info) != mp4parse_status_OK) {
return;
}
@ -809,10 +178,10 @@ TrackTypeEqual(TrackInfo::TrackType aLHS, mp4parse_track_type aRHS)
}
MP4Metadata::ResultAndTrackCount
MP4MetadataRust::GetNumberTracks(mozilla::TrackInfo::TrackType aType) const
MP4Metadata::GetNumberTracks(mozilla::TrackInfo::TrackType aType) const
{
uint32_t tracks;
auto rv = mp4parse_get_track_count(mRustParser.get(), &tracks);
auto rv = mp4parse_get_track_count(mParser.get(), &tracks);
if (rv != mp4parse_status_OK) {
MOZ_LOG(gMP4MetadataLog, LogLevel::Warning,
("rust parser error %d counting tracks", rv));
@ -820,12 +189,11 @@ MP4MetadataRust::GetNumberTracks(mozilla::TrackInfo::TrackType aType) const
RESULT_DETAIL("Rust parser error %d", rv)),
MP4Metadata::NumberTracksError()};
}
MOZ_LOG(gMP4MetadataLog, LogLevel::Info, ("rust parser found %u tracks", tracks));
uint32_t total = 0;
for (uint32_t i = 0; i < tracks; ++i) {
mp4parse_track_info track_info;
rv = mp4parse_get_track_info(mRustParser.get(), i, &track_info);
rv = mp4parse_get_track_info(mParser.get(), i, &track_info);
if (rv != mp4parse_status_OK) {
continue;
}
@ -837,14 +205,18 @@ MP4MetadataRust::GetNumberTracks(mozilla::TrackInfo::TrackType aType) const
}
}
MOZ_LOG(gMP4MetadataLog, LogLevel::Info, ("%s tracks found: %u",
TrackTypeToString(aType),
total));
return {NS_OK, total};
}
Maybe<uint32_t>
MP4MetadataRust::TrackTypeToGlobalTrackIndex(mozilla::TrackInfo::TrackType aType, size_t aTrackNumber) const
MP4Metadata::TrackTypeToGlobalTrackIndex(mozilla::TrackInfo::TrackType aType, size_t aTrackNumber) const
{
uint32_t tracks;
auto rv = mp4parse_get_track_count(mRustParser.get(), &tracks);
auto rv = mp4parse_get_track_count(mParser.get(), &tracks);
if (rv != mp4parse_status_OK) {
return Nothing();
}
@ -856,7 +228,7 @@ MP4MetadataRust::TrackTypeToGlobalTrackIndex(mozilla::TrackInfo::TrackType aType
uint32_t perType = 0;
for (uint32_t i = 0; i < tracks; ++i) {
mp4parse_track_info track_info;
rv = mp4parse_get_track_info(mRustParser.get(), i, &track_info);
rv = mp4parse_get_track_info(mParser.get(), i, &track_info);
if (rv != mp4parse_status_OK) {
continue;
}
@ -872,7 +244,7 @@ MP4MetadataRust::TrackTypeToGlobalTrackIndex(mozilla::TrackInfo::TrackType aType
}
MP4Metadata::ResultAndTrackInfo
MP4MetadataRust::GetTrackInfo(mozilla::TrackInfo::TrackType aType,
MP4Metadata::GetTrackInfo(mozilla::TrackInfo::TrackType aType,
size_t aTrackNumber) const
{
Maybe<uint32_t> trackIndex = TrackTypeToGlobalTrackIndex(aType, aTrackNumber);
@ -884,7 +256,7 @@ MP4MetadataRust::GetTrackInfo(mozilla::TrackInfo::TrackType aType,
}
mp4parse_track_info info;
auto rv = mp4parse_get_track_info(mRustParser.get(), trackIndex.value(), &info);
auto rv = mp4parse_get_track_info(mParser.get(), trackIndex.value(), &info);
if (rv != mp4parse_status_OK) {
MOZ_LOG(gMP4MetadataLog, LogLevel::Warning, ("mp4parse_get_track_info returned %d", rv));
return {MediaResult(NS_ERROR_DOM_MEDIA_METADATA_ERR,
@ -917,7 +289,7 @@ MP4MetadataRust::GetTrackInfo(mozilla::TrackInfo::TrackType aType,
switch (aType) {
case TrackInfo::TrackType::kAudioTrack: {
mp4parse_track_audio_info audio;
auto rv = mp4parse_get_track_audio_info(mRustParser.get(), trackIndex.value(), &audio);
auto rv = mp4parse_get_track_audio_info(mParser.get(), trackIndex.value(), &audio);
if (rv != mp4parse_status_OK) {
MOZ_LOG(gMP4MetadataLog, LogLevel::Warning, ("mp4parse_get_track_audio_info returned error %d", rv));
return {MediaResult(NS_ERROR_DOM_MEDIA_METADATA_ERR,
@ -933,7 +305,7 @@ MP4MetadataRust::GetTrackInfo(mozilla::TrackInfo::TrackType aType,
break;
case TrackInfo::TrackType::kVideoTrack: {
mp4parse_track_video_info video;
auto rv = mp4parse_get_track_video_info(mRustParser.get(), trackIndex.value(), &video);
auto rv = mp4parse_get_track_video_info(mParser.get(), trackIndex.value(), &video);
if (rv != mp4parse_status_OK) {
MOZ_LOG(gMP4MetadataLog, LogLevel::Warning, ("mp4parse_get_track_video_info returned error %d", rv));
return {MediaResult(NS_ERROR_DOM_MEDIA_METADATA_ERR,
@ -959,7 +331,7 @@ MP4MetadataRust::GetTrackInfo(mozilla::TrackInfo::TrackType aType,
// No duration in track, use fragment_duration.
if (e && !e->mDuration.IsPositive()) {
mp4parse_fragment_info info;
auto rv = mp4parse_get_fragment_info(mRustParser.get(), &info);
auto rv = mp4parse_get_fragment_info(mParser.get(), &info);
if (rv == mp4parse_status_OK) {
e->mDuration = TimeUnit::FromMicroseconds(info.fragment_duration);
}
@ -978,50 +350,60 @@ MP4MetadataRust::GetTrackInfo(mozilla::TrackInfo::TrackType aType,
}
bool
MP4MetadataRust::CanSeek() const
MP4Metadata::CanSeek() const
{
MOZ_ASSERT(false, "Not yet implemented");
return false;
return true;
}
MP4Metadata::ResultAndCryptoFile
MP4MetadataRust::Crypto() const
MP4Metadata::Crypto() const
{
return {NS_OK, &mCrypto};
}
MediaResult
MP4MetadataRust::ReadTrackIndice(mp4parse_byte_data* aIndices, mozilla::TrackID aTrackID)
MP4Metadata::ResultAndIndice
MP4Metadata::GetTrackIndice(mozilla::TrackID aTrackID)
{
mp4parse_byte_data indiceRawData = {};
uint8_t fragmented = false;
auto rv = mp4parse_is_fragmented(mRustParser.get(), aTrackID, &fragmented);
auto rv = mp4parse_is_fragmented(mParser.get(), aTrackID, &fragmented);
if (rv != mp4parse_status_OK) {
return MediaResult(NS_ERROR_DOM_MEDIA_METADATA_ERR,
RESULT_DETAIL("Cannot parse whether track id %d is "
"fragmented, mp4parse_error=%d",
int(aTrackID), int(rv)));
return {MediaResult(NS_ERROR_DOM_MEDIA_METADATA_ERR,
RESULT_DETAIL("Cannot parse whether track id %d is "
"fragmented, mp4parse_error=%d",
int(aTrackID), int(rv))),
nullptr};
}
if (fragmented) {
return NS_OK;
if (!fragmented) {
rv = mp4parse_get_indice_table(mParser.get(), aTrackID, &indiceRawData);
if (rv != mp4parse_status_OK) {
return {MediaResult(NS_ERROR_DOM_MEDIA_METADATA_ERR,
RESULT_DETAIL("Cannot parse index table in track id %d, "
"mp4parse_error=%d",
int(aTrackID), int(rv))),
nullptr};
}
}
rv = mp4parse_get_indice_table(mRustParser.get(), aTrackID, aIndices);
if (rv != mp4parse_status_OK) {
return MediaResult(NS_ERROR_DOM_MEDIA_METADATA_ERR,
RESULT_DETAIL("Cannot parse index table in track id %d, "
"mp4parse_error=%d",
int(aTrackID), int(rv)));
}
UniquePtr<IndiceWrapper> indice;
indice = mozilla::MakeUnique<IndiceWrapperRust>(indiceRawData);
return NS_OK;
return {NS_OK, Move(indice)};
}
/*static*/ MP4Metadata::ResultAndByteBuffer
MP4MetadataRust::Metadata(Stream* aSource)
MP4Metadata::Metadata(Stream* aSource)
{
MOZ_ASSERT(false, "Not yet implemented");
return {NS_ERROR_NOT_IMPLEMENTED, nullptr};
auto parser = mozilla::MakeUnique<MoofParser>(aSource, 0, false);
RefPtr<mozilla::MediaByteBuffer> buffer = parser->Metadata();
if (!buffer) {
return {MediaResult(NS_ERROR_DOM_MEDIA_METADATA_ERR,
RESULT_DETAIL("Cannot parse metadata")),
nullptr};
}
return {NS_OK, Move(buffer)};
}
} // namespace mp4_demuxer

Просмотреть файл

@ -5,7 +5,6 @@
#ifndef DECODER_DATA_H_
#define DECODER_DATA_H_
#include "MediaData.h"
#include "MediaInfo.h"
#include "mozilla/RefPtr.h"
#include "mozilla/Result.h"
@ -15,11 +14,6 @@
#include "nsTArray.h"
#include "nsString.h"
namespace stagefright
{
class MetaData;
}
extern "C" {
typedef struct mp4parse_track_info mp4parse_track_info;
typedef struct mp4parse_track_audio_info mp4parse_track_audio_info;
@ -69,9 +63,6 @@ class MP4AudioInfo : public mozilla::AudioInfo
public:
MP4AudioInfo() = default;
void Update(const stagefright::MetaData* aMetaData,
const char* aMimeType);
void Update(const mp4parse_track_info* track,
const mp4parse_track_audio_info* audio);
@ -83,9 +74,6 @@ class MP4VideoInfo : public mozilla::VideoInfo
public:
MP4VideoInfo() = default;
void Update(const stagefright::MetaData* aMetaData,
const char* aMimeType);
void Update(const mp4parse_track_info* track,
const mp4parse_track_video_info* video);

Просмотреть файл

@ -17,9 +17,6 @@
namespace mp4_demuxer {
class MP4MetadataStagefright;
class MP4MetadataRust;
class IndiceWrapper {
public:
virtual size_t Length() const = 0;
@ -31,6 +28,26 @@ public:
virtual ~IndiceWrapper() {}
};
struct FreeMP4Parser { void operator()(mp4parse_parser* aPtr) { mp4parse_free(aPtr); } };
// Wrap an mp4_demuxer::Stream to remember the read offset.
class StreamAdaptor {
public:
explicit StreamAdaptor(Stream* aSource)
: mSource(aSource)
, mOffset(0)
{
}
~StreamAdaptor() {}
bool Read(uint8_t* buffer, uintptr_t size, size_t* bytes_read);
private:
Stream* mSource;
CheckedInt<size_t> mOffset;
};
class MP4Metadata
{
public:
@ -80,13 +97,16 @@ public:
using ResultAndIndice = ResultAndType<mozilla::UniquePtr<IndiceWrapper>>;
ResultAndIndice GetTrackIndice(mozilla::TrackID aTrackID);
nsresult Parse();
private:
UniquePtr<MP4MetadataStagefright> mStagefright;
UniquePtr<MP4MetadataRust> mRust;
mutable bool mDisableRust;
mutable bool mReportedAudioTrackTelemetry;
mutable bool mReportedVideoTrackTelemetry;
bool ShouldPreferRust() const;
void UpdateCrypto();
Maybe<uint32_t> TrackTypeToGlobalTrackIndex(mozilla::TrackInfo::TrackType aType, size_t aTrackNumber) const;
CryptoFile mCrypto;
RefPtr<Stream> mSource;
StreamAdaptor mSourceAdaptor;
mozilla::UniquePtr<mp4parse_parser, FreeMP4Parser> mParser;
};
} // namespace mp4_demuxer

Просмотреть файл

@ -162,6 +162,7 @@ ReadTestFile(const char* aFilename)
struct TestFileData
{
const char* mFilename;
bool mParseResult;
uint32_t mNumberVideoTracks;
bool mHasVideoIndice;
int64_t mVideoDuration; // For first video track, -1 if N/A.
@ -175,202 +176,159 @@ struct TestFileData
bool mHeader;
int8_t mAudioProfile;
};
static const TestFileData testFiles[] = {
// filename #V V-ind dur w h #A dur crypt off moof headr audio_profile
{ "test_case_1156505.mp4", 0, false, -1, 0, 0, 0, -1, false, 152, false, false, 0 },
{ "test_case_1181213.mp4", 0, false, -1, 0, 0, 0, -1, false, 0, false, false, 0 },
{ "test_case_1181215.mp4", 0, false, -1, 0, 0, 0, -1, false, 0, false, false, 0 },
{ "test_case_1181220.mp4", 0, false, -1, 0, 0, 0, -1, false, 0, false, false, 0 },
{ "test_case_1181223.mp4", 0, false, -1, 0, 0, 0, -1, false, 0, false, false, 0 },
{ "test_case_1181719.mp4", 0, false, -1, 0, 0, 0, -1, false, 0, false, false, 0 },
{ "test_case_1185230.mp4", 1, true, 416666,
320, 240, 1, 5, false, 0, false, false, 2 },
{ "test_case_1187067.mp4", 1, true, 80000,
160, 90, 0, -1, false, 0, false, false, 0 },
{ "test_case_1200326.mp4", 0, false, -1, 0, 0, 0, -1, false, 0, false, false, 0 },
{ "test_case_1204580.mp4", 1, true, 502500,
320, 180, 0, -1, false, 0, false, false, 0 },
{ "test_case_1216748.mp4", 0, false, -1, 0, 0, 0, -1, false, 152, false, false, 0 },
{ "test_case_1296473.mp4", 0, false, -1, 0, 0, 0, -1, false, 0, false, false, 0 },
{ "test_case_1296532.mp4", 1, true, 5589333,
560, 320, 1, 5589333,
true, 0, true, true, 2 },
{ "test_case_1301065.mp4", 0, false, -1, 0, 0, 1, 100079991719000000,
false, 0, false, false, 2 },
{ "test_case_1301065-u32max.mp4", 0, false, -1, 0, 0, 1, 97391548639,
false, 0, false, false, 2 },
{ "test_case_1301065-max-ez.mp4", 0, false, -1, 0, 0, 1, 209146758205306,
false, 0, false, false, 2 },
{ "test_case_1301065-harder.mp4", 0, false, -1, 0, 0, 1, 209146758205328,
false, 0, false, false, 2 },
{ "test_case_1301065-max-ok.mp4", 0, false, -1, 0, 0, 1, 9223372036854775804,
false, 0, false, false, 2 },
{ "test_case_1301065-overfl.mp4", 0, false, -1, 0, 0, 0, -1, false, 0, false, false, 0 },
{ "test_case_1301065-i64max.mp4", 0, false, -1, 0, 0, 0, -1, false, 0, false, false, 0 },
{ "test_case_1301065-i64min.mp4", 0, false, -1, 0, 0, 0, -1, false, 0, false, false, 0 },
{ "test_case_1301065-u64max.mp4", 0, false, -1, 0, 0, 1, 0, false, 0, false, false, 2 },
{ "test_case_1329061.mov", 0, false, -1, 0, 0, 1, 234567981,
false, 0, false, false, 2 },
{ "test_case_1351094.mp4", 0, false, -1, 0, 0, 0, -1, false, 0, true, true, 0 },
};
static const TestFileData rustTestFiles[] = {
// filename #V dur w h #A dur crypt off moof headr audio_profile
{ "test_case_1156505.mp4", 0, false, -1, 0, 0, 0, -1, false, 152, false, false, 0 },
{ "test_case_1181213.mp4", 1, true, 416666,
static const TestFileData testFiles[] = {
// filename parse #V dur w h #A dur crypt off moof headr audio_profile
{ "test_case_1156505.mp4", false, 0, false, -1, 0, 0, 0, -1, false, 152, false, false, 0 }, // invalid ''trak box
{ "test_case_1181213.mp4", true, 1, true, 416666,
320, 240, 1, 477460,
true, 0, false, false, 2 },
{ "test_case_1181215.mp4", 0, false, -1, 0, 0, 0, -1, false, 0, false, false, 0 },
{ "test_case_1181220.mp4", 0, false, -1, 0, 0, 0, -1, false, 0, false, false, 0 },
{ "test_case_1181223.mp4", 0, false, 416666,
{ "test_case_1181215.mp4", true, 0, false, -1, 0, 0, 0, -1, false, 0, false, false, 0 },
{ "test_case_1181220.mp4", false, 0, false, -1, 0, 0, 0, -1, false, 0, false, false, 0 }, // invalid audio 'sinf' box
{ "test_case_1181223.mp4", false, 0, false, 416666,
320, 240, 0, -1, false, 0, false, false, 0 },
{ "test_case_1181719.mp4", 0, false, -1, 0, 0, 0, -1, false, 0, false, false, 0 },
{ "test_case_1185230.mp4", 2, true, 416666,
{ "test_case_1181719.mp4", false, 0, false, -1, 0, 0, 0, -1, false, 0, false, false, 0 },
{ "test_case_1185230.mp4", true, 2, true, 416666,
320, 240, 2, 5, false, 0, false, false, 2 },
{ "test_case_1187067.mp4", 1, true, 80000,
{ "test_case_1187067.mp4", true, 1, true, 80000,
160, 90, 0, -1, false, 0, false, false, 0 },
{ "test_case_1200326.mp4", 0, false, -1, 0, 0, 0, -1, false, 0, false, false, 0 },
{ "test_case_1204580.mp4", 1, true, 502500,
{ "test_case_1200326.mp4", false, 0, false, -1, 0, 0, 0, -1, false, 0, false, false, 0 },
{ "test_case_1204580.mp4", true, 1, true, 502500,
320, 180, 0, -1, false, 0, false, false, 0 },
{ "test_case_1216748.mp4", 0, false, -1, 0, 0, 0, -1, false, 152, false, false, 0 },
{ "test_case_1296473.mp4", 0, false, -1, 0, 0, 0, -1, false, 0, false, false, 0 },
{ "test_case_1296532.mp4", 1, true, 5589333,
{ "test_case_1216748.mp4", false, 0, false, -1, 0, 0, 0, -1, false, 152, false, false, 0 }, // invalid 'trak' box
{ "test_case_1296473.mp4", false, 0, false, -1, 0, 0, 0, -1, false, 0, false, false, 0 },
{ "test_case_1296532.mp4", true, 1, true, 5589333,
560, 320, 1, 5589333,
true, 0, true, true, 2 },
{ "test_case_1301065.mp4", 0, false, -1, 0, 0, 1, 100079991719000000,
{ "test_case_1301065.mp4", true, 0, false, -1, 0, 0, 1, 100079991719000000,
false, 0, false, false, 2 },
{ "test_case_1301065-u32max.mp4", 0, false, -1, 0, 0, 1, 97391548639,
{ "test_case_1301065-u32max.mp4", true, 0, false, -1, 0, 0, 1, 97391548639,
false, 0, false, false, 2 },
{ "test_case_1301065-max-ez.mp4", 0, false, -1, 0, 0, 1, 209146758205306,
{ "test_case_1301065-max-ez.mp4", true, 0, false, -1, 0, 0, 1, 209146758205306,
false, 0, false, false, 2 },
{ "test_case_1301065-harder.mp4", 0, false, -1, 0, 0, 1, 209146758205328,
{ "test_case_1301065-harder.mp4", true, 0, false, -1, 0, 0, 1, 209146758205328,
false, 0, false, false, 2 },
{ "test_case_1301065-max-ok.mp4", 0, false, -1, 0, 0, 1, 9223372036854775804,
{ "test_case_1301065-max-ok.mp4", true, 0, false, -1, 0, 0, 1, 9223372036854775804,
false, 0, false, false, 2 },
// The duration is overflow for int64_t in TestFileData, rust parser uses uint64_t so
// The duration is overflow for int64_t in TestFileData, parser uses uint64_t so
// this file is ignore.
//{ "test_case_1301065-overfl.mp4", 0, -1, 0, 0, 1, 9223372036854775827,
// false, 0, false, false, 2 },
{ "test_case_1301065-i64max.mp4", 0, false, -1, 0, 0, 0, -1, false, 0, false, false, 0 },
{ "test_case_1301065-i64min.mp4", 0, false, -1, 0, 0, 0, -1, false, 0, false, false, 0 },
{ "test_case_1301065-u64max.mp4", 0, false, -1, 0, 0, 1, 0, false, 0, false, false, 2 },
{ "test_case_1329061.mov", 0, false, -1, 0, 0, 1, 234567981,
{ "test_case_1301065-i64max.mp4", true, 0, false, -1, 0, 0, 0, -1, false, 0, false, false, 0 },
{ "test_case_1301065-i64min.mp4", true, 0, false, -1, 0, 0, 0, -1, false, 0, false, false, 0 },
{ "test_case_1301065-u64max.mp4", true, 0, false, -1, 0, 0, 1, 0, false, 0, false, false, 2 },
{ "test_case_1329061.mov", false, 0, false, -1, 0, 0, 1, 234567981,
false, 0, false, false, 2 },
{ "test_case_1351094.mp4", 0, false, -1, 0, 0, 0, -1, false, 0, true, true, 0 },
{ "test_case_1389299.mp4", 1, true, 5589333,
{ "test_case_1351094.mp4", true, 0, false, -1, 0, 0, 0, -1, false, 0, true, true, 0 },
{ "test_case_1389299.mp4", true, 1, true, 5589333,
560, 320, 1, 5589333,
true, 0, true, true, 2 },
{ "test_case_1389527.mp4", 1, false, 5005000,
{ "test_case_1389527.mp4", true, 1, false, 5005000,
80, 128, 1, 4992000, false, 0, false, false, 2 },
{ "test_case_1395244.mp4", 1, true, 416666,
{ "test_case_1395244.mp4", true, 1, true, 416666,
320, 240, 1,477460, false,0, false, false, 2 },
{ "test_case_1388991.mp4", 0, false, -1, 0, 0, 1, 30000181, false, 0, false, false, 2 },
{ "test_case_1380468.mp4", 0, false, 0, 0, 0, 0, 0, false, 0, false, false, 0 },
{ "test_case_1410565.mp4", 1, true, 0,
320, 180, 1, 0, false, 955100, true, true, 2 },
{ "test_case_1388991.mp4", true, 0, false, -1, 0, 0, 1, 30000181, false, 0, false, false, 2 },
{ "test_case_1380468.mp4", false, 0, false, 0, 0, 0, 0, 0, false, 0, false, false, 0 },
{ "test_case_1410565.mp4", false, 0, false, 0, 0, 0, 0, 0, false, 955100, true, true, 2 }, // negative 'timescale'
};
TEST(stagefright_MPEG4Metadata, test_case_mp4)
{
for (bool rust : { !MediaPrefs::EnableRustMP4Parser(),
MediaPrefs::EnableRustMP4Parser() }) {
mozilla::Preferences::SetBool("media.rust.mp4parser", rust);
ASSERT_EQ(rust, MediaPrefs::EnableRustMP4Parser());
const TestFileData* tests = nullptr;
size_t length = 0;
const TestFileData* tests = nullptr;
size_t length = 0;
tests = testFiles;
length = ArrayLength(testFiles);
if (rust) {
tests = rustTestFiles;
length = ArrayLength(rustTestFiles);
} else {
tests = testFiles;
length = ArrayLength(testFiles);
for (size_t test = 0; test < length; ++test) {
nsTArray<uint8_t> buffer = ReadTestFile(tests[test].mFilename);
ASSERT_FALSE(buffer.IsEmpty());
RefPtr<Stream> stream = new TestStream(buffer.Elements(), buffer.Length());
MP4Metadata::ResultAndByteBuffer metadataBuffer =
MP4Metadata::Metadata(stream);
EXPECT_EQ(NS_OK, metadataBuffer.Result());
EXPECT_TRUE(metadataBuffer.Ref());
MP4Metadata metadata(stream);
nsresult res = metadata.Parse();
EXPECT_EQ(tests[test].mParseResult, NS_SUCCEEDED(res)) << tests[test].mFilename;
if (!tests[test].mParseResult) {
continue;
}
for (size_t test = 0; test < length; ++test) {
nsTArray<uint8_t> buffer = ReadTestFile(tests[test].mFilename);
ASSERT_FALSE(buffer.IsEmpty());
RefPtr<Stream> stream = new TestStream(buffer.Elements(), buffer.Length());
EXPECT_EQ(tests[test].mNumberAudioTracks,
metadata.GetNumberTracks(TrackInfo::kAudioTrack).Ref())
<< tests[test].mFilename;
EXPECT_EQ(tests[test].mNumberVideoTracks,
metadata.GetNumberTracks(TrackInfo::kVideoTrack).Ref())
<< tests[test].mFilename;
// If there is an error, we should expect an error code instead of zero
// for non-Audio/Video tracks.
const uint32_t None = (tests[test].mNumberVideoTracks == E) ? E : 0;
EXPECT_EQ(None, metadata.GetNumberTracks(TrackInfo::kUndefinedTrack).Ref())
<< tests[test].mFilename;
EXPECT_EQ(None, metadata.GetNumberTracks(TrackInfo::kTextTrack).Ref())
<< tests[test].mFilename;
EXPECT_FALSE(metadata.GetTrackInfo(TrackInfo::kUndefinedTrack, 0).Ref());
MP4Metadata::ResultAndTrackInfo trackInfo =
metadata.GetTrackInfo(TrackInfo::kVideoTrack, 0);
if (!!tests[test].mNumberVideoTracks) {
ASSERT_TRUE(!!trackInfo.Ref());
const VideoInfo* videoInfo = trackInfo.Ref()->GetAsVideoInfo();
ASSERT_TRUE(!!videoInfo);
EXPECT_TRUE(videoInfo->IsValid()) << tests[test].mFilename;
EXPECT_TRUE(videoInfo->IsVideo()) << tests[test].mFilename;
EXPECT_EQ(tests[test].mVideoDuration,
videoInfo->mDuration.ToMicroseconds()) << tests[test].mFilename;
EXPECT_EQ(tests[test].mWidth, videoInfo->mDisplay.width) << tests[test].mFilename;
EXPECT_EQ(tests[test].mHeight, videoInfo->mDisplay.height) << tests[test].mFilename;
MP4Metadata::ResultAndByteBuffer metadataBuffer =
MP4Metadata::Metadata(stream);
EXPECT_EQ(NS_OK, metadataBuffer.Result());
EXPECT_TRUE(metadataBuffer.Ref());
MP4Metadata metadata(stream);
EXPECT_EQ(tests[test].mNumberAudioTracks,
metadata.GetNumberTracks(TrackInfo::kAudioTrack).Ref())
<< (rust ? "rust/" : "stagefright/") << tests[test].mFilename;
EXPECT_EQ(tests[test].mNumberVideoTracks,
metadata.GetNumberTracks(TrackInfo::kVideoTrack).Ref())
<< (rust ? "rust/" : "stagefright/") << tests[test].mFilename;
// If there is an error, we should expect an error code instead of zero
// for non-Audio/Video tracks.
const uint32_t None = (tests[test].mNumberVideoTracks == E) ? E : 0;
EXPECT_EQ(None, metadata.GetNumberTracks(TrackInfo::kUndefinedTrack).Ref())
<< (rust ? "rust/" : "stagefright/") << tests[test].mFilename;
EXPECT_EQ(None, metadata.GetNumberTracks(TrackInfo::kTextTrack).Ref())
<< (rust ? "rust/" : "stagefright/") << tests[test].mFilename;
EXPECT_FALSE(metadata.GetTrackInfo(TrackInfo::kUndefinedTrack, 0).Ref());
MP4Metadata::ResultAndTrackInfo trackInfo =
metadata.GetTrackInfo(TrackInfo::kVideoTrack, 0);
if (!!tests[test].mNumberVideoTracks) {
ASSERT_TRUE(!!trackInfo.Ref());
const VideoInfo* videoInfo = trackInfo.Ref()->GetAsVideoInfo();
ASSERT_TRUE(!!videoInfo);
EXPECT_TRUE(videoInfo->IsValid()) << (rust ? "rust/" : "stagefright/") << tests[test].mFilename;
EXPECT_TRUE(videoInfo->IsVideo()) << (rust ? "rust/" : "stagefright/") << tests[test].mFilename;
EXPECT_EQ(tests[test].mVideoDuration,
videoInfo->mDuration.ToMicroseconds()) << (rust ? "rust/" : "stagefright/") << tests[test].mFilename;
EXPECT_EQ(tests[test].mWidth, videoInfo->mDisplay.width) << (rust ? "rust/" : "stagefright/") << tests[test].mFilename;
EXPECT_EQ(tests[test].mHeight, videoInfo->mDisplay.height) << (rust ? "rust/" : "stagefright/") << tests[test].mFilename;
MP4Metadata::ResultAndIndice indices =
metadata.GetTrackIndice(videoInfo->mTrackId);
EXPECT_EQ(!!indices.Ref(), tests[test].mHasVideoIndice) << (rust ? "rust/" : "stagefright/") << tests[test].mFilename;
if (tests[test].mHasVideoIndice) {
for (size_t i = 0; i < indices.Ref()->Length(); i++) {
Index::Indice data;
EXPECT_TRUE(indices.Ref()->GetIndice(i, data)) << (rust ? "rust/" : "stagefright/") << tests[test].mFilename;
EXPECT_TRUE(data.start_offset <= data.end_offset) << (rust ? "rust/" : "stagefright/") << tests[test].mFilename;
EXPECT_TRUE(data.start_composition <= data.end_composition) << (rust ? "rust/" : "stagefright/") << tests[test].mFilename;
}
}
}
trackInfo = metadata.GetTrackInfo(TrackInfo::kAudioTrack, 0);
if (tests[test].mNumberAudioTracks == 0 ||
tests[test].mNumberAudioTracks == E) {
EXPECT_TRUE(!trackInfo.Ref()) << (rust ? "rust/" : "stagefright/") << tests[test].mFilename;
} else {
ASSERT_TRUE(!!trackInfo.Ref());
const AudioInfo* audioInfo = trackInfo.Ref()->GetAsAudioInfo();
ASSERT_TRUE(!!audioInfo);
EXPECT_TRUE(audioInfo->IsValid()) << (rust ? "rust/" : "stagefright/") << tests[test].mFilename;
EXPECT_TRUE(audioInfo->IsAudio()) << (rust ? "rust/" : "stagefright/") << tests[test].mFilename;
EXPECT_EQ(tests[test].mAudioDuration,
audioInfo->mDuration.ToMicroseconds()) << (rust ? "rust/" : "stagefright/") << tests[test].mFilename;
EXPECT_EQ(tests[test].mAudioProfile, audioInfo->mProfile) << (rust ? "rust/" : "stagefright/") << tests[test].mFilename;
if (tests[test].mAudioDuration !=
audioInfo->mDuration.ToMicroseconds()) {
MOZ_RELEASE_ASSERT(false);
}
MP4Metadata::ResultAndIndice indices =
metadata.GetTrackIndice(audioInfo->mTrackId);
EXPECT_TRUE(!!indices.Ref()) << (rust ? "rust/" : "stagefright/") << tests[test].mFilename;
MP4Metadata::ResultAndIndice indices =
metadata.GetTrackIndice(videoInfo->mTrackId);
EXPECT_EQ(!!indices.Ref(), tests[test].mHasVideoIndice) << tests[test].mFilename;
if (tests[test].mHasVideoIndice) {
for (size_t i = 0; i < indices.Ref()->Length(); i++) {
Index::Indice data;
EXPECT_TRUE(indices.Ref()->GetIndice(i, data)) << (rust ? "rust/" : "stagefright/") << tests[test].mFilename;
EXPECT_TRUE(data.start_offset <= data.end_offset) << (rust ? "rust/" : "stagefright/") << tests[test].mFilename;
EXPECT_TRUE(int64_t(data.start_composition) <= int64_t(data.end_composition)) << (rust ? "rust/" : "stagefright/") << tests[test].mFilename;
EXPECT_TRUE(indices.Ref()->GetIndice(i, data)) << tests[test].mFilename;
EXPECT_TRUE(data.start_offset <= data.end_offset) << tests[test].mFilename;
EXPECT_TRUE(data.start_composition <= data.end_composition) << tests[test].mFilename;
}
}
EXPECT_FALSE(metadata.GetTrackInfo(TrackInfo::kTextTrack, 0).Ref()) << (rust ? "rust/" : "stagefright/") << tests[test].mFilename;
// We can see anywhere in any MPEG4.
EXPECT_TRUE(metadata.CanSeek()) << (rust ? "rust/" : "stagefright/") << tests[test].mFilename;
EXPECT_EQ(tests[test].mHasCrypto, metadata.Crypto().Ref()->valid) << (rust ? "rust/" : "stagefright/") << tests[test].mFilename;
}
trackInfo = metadata.GetTrackInfo(TrackInfo::kAudioTrack, 0);
if (tests[test].mNumberAudioTracks == 0 ||
tests[test].mNumberAudioTracks == E) {
EXPECT_TRUE(!trackInfo.Ref()) << tests[test].mFilename;
} else {
ASSERT_TRUE(!!trackInfo.Ref());
const AudioInfo* audioInfo = trackInfo.Ref()->GetAsAudioInfo();
ASSERT_TRUE(!!audioInfo);
EXPECT_TRUE(audioInfo->IsValid()) << tests[test].mFilename;
EXPECT_TRUE(audioInfo->IsAudio()) << tests[test].mFilename;
EXPECT_EQ(tests[test].mAudioDuration,
audioInfo->mDuration.ToMicroseconds()) << tests[test].mFilename;
EXPECT_EQ(tests[test].mAudioProfile, audioInfo->mProfile) << tests[test].mFilename;
if (tests[test].mAudioDuration !=
audioInfo->mDuration.ToMicroseconds()) {
MOZ_RELEASE_ASSERT(false);
}
MP4Metadata::ResultAndIndice indices =
metadata.GetTrackIndice(audioInfo->mTrackId);
EXPECT_TRUE(!!indices.Ref()) << tests[test].mFilename;
for (size_t i = 0; i < indices.Ref()->Length(); i++) {
Index::Indice data;
EXPECT_TRUE(indices.Ref()->GetIndice(i, data)) << tests[test].mFilename;
EXPECT_TRUE(data.start_offset <= data.end_offset) << tests[test].mFilename;
EXPECT_TRUE(int64_t(data.start_composition) <= int64_t(data.end_composition)) << tests[test].mFilename;
}
}
EXPECT_FALSE(metadata.GetTrackInfo(TrackInfo::kTextTrack, 0).Ref()) << tests[test].mFilename;
// We can see anywhere in any MPEG4.
EXPECT_TRUE(metadata.CanSeek()) << tests[test].mFilename;
EXPECT_EQ(tests[test].mHasCrypto, metadata.Crypto().Ref()->valid) << tests[test].mFilename;
}
}
@ -415,55 +373,44 @@ TEST(stagefright_MPEG4Metadata, test_case_mp4_subsets)
TEST(stagefright_MoofParser, test_case_mp4)
{
for (bool rust : { !MediaPrefs::EnableRustMP4Parser(),
MediaPrefs::EnableRustMP4Parser() }) {
mozilla::Preferences::SetBool("media.rust.mp4parser", rust);
ASSERT_EQ(rust, MediaPrefs::EnableRustMP4Parser());
const TestFileData* tests = nullptr;
size_t length = 0;
const TestFileData* tests = nullptr;
size_t length = 0;
tests = testFiles;
length = ArrayLength(testFiles);
if (rust) {
tests = rustTestFiles;
length = ArrayLength(rustTestFiles);
for (size_t test = 0; test < length; ++test) {
nsTArray<uint8_t> buffer = ReadTestFile(tests[test].mFilename);
ASSERT_FALSE(buffer.IsEmpty());
RefPtr<Stream> stream = new TestStream(buffer.Elements(), buffer.Length());
MoofParser parser(stream, 0, false);
EXPECT_EQ(0u, parser.mOffset) << tests[test].mFilename;
EXPECT_FALSE(parser.ReachedEnd()) << tests[test].mFilename;
EXPECT_TRUE(parser.mInitRange.IsEmpty()) << tests[test].mFilename;
EXPECT_TRUE(parser.HasMetadata()) << tests[test].mFilename;
RefPtr<MediaByteBuffer> metadataBuffer = parser.Metadata();
EXPECT_TRUE(metadataBuffer) << tests[test].mFilename;
EXPECT_FALSE(parser.mInitRange.IsEmpty()) << tests[test].mFilename;
const MediaByteRangeSet byteRanges(
MediaByteRange(0, int64_t(buffer.Length())));
EXPECT_EQ(tests[test].mValidMoof,
parser.RebuildFragmentedIndex(byteRanges)) << tests[test].mFilename;
if (tests[test].mMoofReachedOffset == 0) {
EXPECT_EQ(buffer.Length(), parser.mOffset) << tests[test].mFilename;
EXPECT_TRUE(parser.ReachedEnd()) << tests[test].mFilename;
} else {
tests = testFiles;
length = ArrayLength(testFiles);
EXPECT_EQ(tests[test].mMoofReachedOffset, parser.mOffset) << tests[test].mFilename;
EXPECT_FALSE(parser.ReachedEnd()) << tests[test].mFilename;
}
for (size_t test = 0; test < length; ++test) {
nsTArray<uint8_t> buffer = ReadTestFile(tests[test].mFilename);
ASSERT_FALSE(buffer.IsEmpty());
RefPtr<Stream> stream = new TestStream(buffer.Elements(), buffer.Length());
MoofParser parser(stream, 0, false);
EXPECT_EQ(0u, parser.mOffset);
EXPECT_FALSE(parser.ReachedEnd());
EXPECT_TRUE(parser.mInitRange.IsEmpty());
EXPECT_TRUE(parser.HasMetadata());
RefPtr<MediaByteBuffer> metadataBuffer = parser.Metadata();
EXPECT_TRUE(metadataBuffer);
EXPECT_FALSE(parser.mInitRange.IsEmpty());
const MediaByteRangeSet byteRanges(
MediaByteRange(0, int64_t(buffer.Length())));
EXPECT_EQ(tests[test].mValidMoof,
parser.RebuildFragmentedIndex(byteRanges));
if (tests[test].mMoofReachedOffset == 0) {
EXPECT_EQ(buffer.Length(), parser.mOffset);
EXPECT_TRUE(parser.ReachedEnd());
} else {
EXPECT_EQ(tests[test].mMoofReachedOffset, parser.mOffset);
EXPECT_FALSE(parser.ReachedEnd());
}
EXPECT_FALSE(parser.mInitRange.IsEmpty());
EXPECT_TRUE(parser.GetCompositionRange(byteRanges).IsNull());
EXPECT_TRUE(parser.FirstCompleteMediaSegment().IsEmpty());
EXPECT_EQ(tests[test].mHeader,
!parser.FirstCompleteMediaHeader().IsEmpty());
}
EXPECT_FALSE(parser.mInitRange.IsEmpty()) << tests[test].mFilename;
EXPECT_TRUE(parser.GetCompositionRange(byteRanges).IsNull()) << tests[test].mFilename;
EXPECT_TRUE(parser.FirstCompleteMediaSegment().IsEmpty()) << tests[test].mFilename;
EXPECT_EQ(tests[test].mHeader,
!parser.FirstCompleteMediaHeader().IsEmpty()) << tests[test].mFilename;
}
}
@ -511,7 +458,7 @@ TEST(stagefright_MoofParser, test_case_mp4_subsets)
}
#endif
uint8_t media_libstagefright_gtest_video_init_mp4[] = {
uint8_t media_gtest_video_init_mp4[] = {
0x00, 0x00, 0x00, 0x18, 0x66, 0x74, 0x79, 0x70, 0x69, 0x73, 0x6f, 0x6d,
0x00, 0x00, 0x00, 0x01, 0x69, 0x73, 0x6f, 0x6d, 0x61, 0x76, 0x63, 0x31,
0x00, 0x00, 0x02, 0xd1, 0x6d, 0x6f, 0x6f, 0x76, 0x00, 0x00, 0x00, 0x6c,
@ -577,12 +524,12 @@ uint8_t media_libstagefright_gtest_video_init_mp4[] = {
0x00
};
const uint32_t media_libstagefright_gtest_video_init_mp4_len = 745;
const uint32_t media_gtest_video_init_mp4_len = 745;
TEST(stagefright_MP4Metadata, EmptyCTTS)
{
RefPtr<MediaByteBuffer> buffer = new MediaByteBuffer(media_libstagefright_gtest_video_init_mp4_len);
buffer->AppendElements(media_libstagefright_gtest_video_init_mp4, media_libstagefright_gtest_video_init_mp4_len);
RefPtr<MediaByteBuffer> buffer = new MediaByteBuffer(media_gtest_video_init_mp4_len);
buffer->AppendElements(media_gtest_video_init_mp4, media_gtest_video_init_mp4_len);
RefPtr<BufferStream> stream = new BufferStream(buffer);
MP4Metadata::ResultAndByteBuffer metadataBuffer =
@ -591,7 +538,7 @@ TEST(stagefright_MP4Metadata, EmptyCTTS)
EXPECT_TRUE(metadataBuffer.Ref());
MP4Metadata metadata(stream);
EXPECT_EQ(metadata.Parse(), NS_OK);
EXPECT_EQ(1u, metadata.GetNumberTracks(TrackInfo::kVideoTrack).Ref());
MP4Metadata::ResultAndTrackInfo track = metadata.GetTrackInfo(TrackInfo::kVideoTrack, 0);
EXPECT_TRUE(track.Ref() != nullptr);

Просмотреть файл

@ -7,52 +7,6 @@
with Files("**"):
BUG_COMPONENT = ("Core", "Audio/Video: Playback")
DEFINES['ANDROID_SMP'] = 0
DEFINES['LOG_NDEBUG'] = 1
if CONFIG['OS_TARGET'] != 'WINNT' and CONFIG['OS_TARGET'] != 'SunOS':
DEFINES['_GLIBCXX_OS_DEFINES'] = True
if CONFIG['OS_TARGET'] == 'WINNT':
if CONFIG['_MSC_VER']:
DEFINES['ssize_t'] = 'intptr_t'
DEFINES['off64_t'] = 'int64_t'
DEFINES['strcasecmp'] = 'stricmp'
DEFINES['strncasecmp'] = 'strnicmp'
DEFINES['HAVE_MS_C_RUNTIME'] = True
DEFINES['__PRETTY_FUNCTION__'] = '__FUNCTION__'
if CONFIG['CC_TYPE'] == 'gcc':
LOCAL_INCLUDES += [ 'ports/mingw32/include' ]
else:
LOCAL_INCLUDES += [ 'ports/win32/include' ]
elif CONFIG['OS_TARGET'] == 'Darwin':
DEFINES['HAVE_SYS_UIO_H'] = True
DEFINES['off64_t'] = 'off_t'
LOCAL_INCLUDES += [ 'ports/darwin/include' ]
elif CONFIG['OS_TARGET'] in ('DragonFly', 'FreeBSD', 'OpenBSD', 'NetBSD',
'GNU/kFreeBSD'):
if CONFIG['OS_TARGET'] != 'NetBSD':
DEFINES['ENODATA'] = '-0x80000003'
if CONFIG['OS_TARGET'] == 'OpenBSD':
DEFINES['EBADMSG'] = '-0x80000006'
DEFINES['HAVE_SYS_UIO_H'] = True
if CONFIG['OS_TARGET'] != 'GNU/kFreeBSD':
DEFINES['off64_t'] = 'off_t'
LOCAL_INCLUDES += [ 'ports/bsd/include' ]
else:
DEFINES['HAVE_SYS_UIO_H'] = True
if CONFIG['OS_TARGET'] != 'Android':
DEFINES['FAKE_LOG_DEVICE'] = True
SOURCES += [
'system/core/liblog/fake_log_device.c',
]
UNIFIED_SOURCES += [
'system/core/libcutils/strdup16to8.c',
'system/core/liblog/logd_write.c',
'system/core/liblog/logprint.c',
]
EXPORTS.mp4_demuxer += [
'binding/include/mp4_demuxer/Adts.h',
'binding/include/mp4_demuxer/AnnexB.h',
@ -77,15 +31,6 @@ EXPORTS.demuxer += [
'binding/include/demuxer/TrackDemuxer.h',
]
SOURCES += [
'frameworks/av/media/libstagefright/foundation/hexdump.cpp',
'frameworks/av/media/libstagefright/MetaData.cpp',
'system/core/libutils/RefBase.cpp',
'system/core/libutils/String16.cpp',
'system/core/libutils/String8.cpp',
'system/core/libutils/VectorImpl.cpp',
]
EXPORTS += [
'binding/include/mp4parse.h',
]
@ -103,42 +48,16 @@ UNIFIED_SOURCES += [
'binding/MP4Metadata.cpp',
'binding/ResourceStream.cpp',
'binding/SinfParser.cpp',
'frameworks/av/media/libstagefright/DataSource.cpp',
'frameworks/av/media/libstagefright/ESDS.cpp',
'frameworks/av/media/libstagefright/foundation/AAtomizer.cpp',
'frameworks/av/media/libstagefright/foundation/ABitReader.cpp',
'frameworks/av/media/libstagefright/foundation/ABuffer.cpp',
'frameworks/av/media/libstagefright/foundation/AString.cpp',
'frameworks/av/media/libstagefright/MediaBuffer.cpp',
'frameworks/av/media/libstagefright/MediaDefs.cpp',
'frameworks/av/media/libstagefright/MediaSource.cpp',
'frameworks/av/media/libstagefright/MPEG4Extractor.cpp',
'frameworks/av/media/libstagefright/SampleIterator.cpp',
'frameworks/av/media/libstagefright/SampleTable.cpp',
'frameworks/av/media/libstagefright/Utils.cpp',
'system/core/libutils/SharedBuffer.cpp',
'system/core/libutils/Static.cpp',
'system/core/libutils/Unicode.cpp',
]
LOCAL_INCLUDES += [
'binding/include',
'frameworks/av/include',
'frameworks/av/include/media/stagefright/foundation',
'frameworks/av/media/libstagefright/',
'stubs/empty',
'stubs/include',
'stubs/include/media/stagefright/foundation',
'system/core/include',
]
TEST_DIRS += [
'gtest',
]
# We allow warnings for third-party code that can be updated from upstream.
AllowCompilerWarnings()
FINAL_LIBRARY = 'xul'
# Suppress warnings in third-party code.

Просмотреть файл

@ -31,10 +31,10 @@ mac-rel-wpt4:
mac-dev-unit:
- ./mach clean-nightlies --keep 3 --force
- env PKG_CONFIG_PATH=/usr/local/opt/zlib/lib/pkgconfig SERVO_RUSTC_LLVM_ASSERTIONS=1 ./mach build --dev
- env SERVO_RUSTC_LLVM_ASSERTIONS=1 ./mach test-unit
- env SERVO_RUSTC_LLVM_ASSERTIONS=1 ./mach package --dev
- env SERVO_RUSTC_LLVM_ASSERTIONS=1 ./mach build-cef
- env PKG_CONFIG_PATH=/usr/local/opt/zlib/lib/pkgconfig ./mach build --dev
- env ./mach test-unit
- env ./mach package --dev
- env ./mach build-cef
- ./mach build-geckolib
- bash ./etc/ci/lockfile_changed.sh
- bash ./etc/ci/manifest_changed.sh
@ -78,12 +78,12 @@ linux-dev:
- ./mach clean-nightlies --keep 3 --force
- ./mach test-tidy --no-progress --all
- ./mach test-tidy --no-progress --self-test
- env SERVO_RUSTC_LLVM_ASSERTIONS=1 ./mach build --dev
- env SERVO_RUSTC_LLVM_ASSERTIONS=1 ./mach test-compiletest
- env SERVO_RUSTC_LLVM_ASSERTIONS=1 ./mach test-unit
- env SERVO_RUSTC_LLVM_ASSERTIONS=1 ./mach package --dev
- env SERVO_RUSTC_LLVM_ASSERTIONS=1 ./mach build-cef
- env SERVO_RUSTC_LLVM_ASSERTIONS=1 ./mach build --dev --no-default-features --features default-except-unstable
- env ./mach build --dev
- env ./mach test-compiletest
- env ./mach test-unit
- env ./mach package --dev
- env ./mach build-cef
- env ./mach build --dev --no-default-features --features default-except-unstable
- ./mach build-geckolib
- ./mach test-stylo
- bash ./etc/ci/lockfile_changed.sh
@ -116,27 +116,27 @@ linux-nightly:
android:
- ./mach clean-nightlies --keep 3 --force
- env SERVO_RUSTC_LLVM_ASSERTIONS=1 ANDROID_SDK=/home/servo/android/sdk/r25.2.3 ./mach build --android --dev
- env SERVO_RUSTC_LLVM_ASSERTIONS=1 ANDROID_SDK=/home/servo/android/sdk/r25.2.3 ./mach package --android --dev
- env ANDROID_SDK=/home/servo/android/sdk/r25.2.3 ./mach build --android --dev
- env ANDROID_SDK=/home/servo/android/sdk/r25.2.3 ./mach package --android --dev
- bash ./etc/ci/lockfile_changed.sh
- bash ./etc/ci/manifest_changed.sh
- python ./etc/ci/check_dynamic_symbols.py
android-nightly:
- ./mach clean-nightlies --keep 3 --force
- env SERVO_RUSTC_LLVM_ASSERTIONS=1 ANDROID_SDK=/home/servo/android/sdk/r25.2.3 ./mach build --android --release
- env SERVO_RUSTC_LLVM_ASSERTIONS=1 ANDROID_SDK=/home/servo/android/sdk/r25.2.3 ./mach package --android --release
- env ANDROID_SDK=/home/servo/android/sdk/r25.2.3 ./mach build --android --release
- env ANDROID_SDK=/home/servo/android/sdk/r25.2.3 ./mach package --android --release
- ./mach upload-nightly android
arm32:
- ./mach clean-nightlies --keep 3 --force
- env SERVO_RUSTC_LLVM_ASSERTIONS=1 ./mach build --rel --target=arm-unknown-linux-gnueabihf
- env ./mach build --rel --target=arm-unknown-linux-gnueabihf
- bash ./etc/ci/lockfile_changed.sh
- bash ./etc/ci/manifest_changed.sh
arm64:
- ./mach clean-nightlies --keep 3 --force
- env SERVO_RUSTC_LLVM_ASSERTIONS=1 ./mach build --rel --target=aarch64-unknown-linux-gnu
- env ./mach build --rel --target=aarch64-unknown-linux-gnu
- bash ./etc/ci/lockfile_changed.sh
- bash ./etc/ci/manifest_changed.sh

Просмотреть файл

@ -29,7 +29,7 @@ from mach.decorators import (
import servo.bootstrap as bootstrap
from servo.command_base import CommandBase, BIN_SUFFIX, cd
from servo.util import delete, download_bytes, download_file, extract, host_triple
from servo.util import STATIC_RUST_LANG_ORG_DIST, URLOPEN_KWARGS
from servo.util import STATIC_RUST_LANG_ORG_DIST
@CommandProvider
@ -90,15 +90,8 @@ class MachCommands(CommandBase):
# in that directory).
if stable:
base_url = STATIC_RUST_LANG_ORG_DIST
elif self.config["build"]["llvm-assertions"]:
base_url = nightly_dist
else:
import toml
channel = nightly_dist + "/channel-rust-nightly.toml"
manifest = toml.load(urllib2.urlopen(channel, **URLOPEN_KWARGS))
nightly_commit_hash = manifest["pkg"]["rustc"]["git_commit_hash"]
base_url = "https://s3.amazonaws.com/rust-lang-ci/rustc-builds-alt/" + nightly_commit_hash
base_url = nightly_dist
rustc_url = base_url + "/rustc-%s-%s.tar.gz" % (version, host_triple())
tgz_file = rust_dir + '-rustc.tar.gz'

Просмотреть файл

@ -260,19 +260,9 @@ class CommandBase(object):
self.config["tools"].setdefault("cargo-root", "")
self.config["tools"].setdefault("rustc-with-gold", get_env_bool("SERVO_RUSTC_WITH_GOLD", True))
# https://github.com/rust-lang/rust/pull/39754
triples_with_rustc_alt_builds = [
"x86_64-unknown-linux-gnu",
"x86_64-apple-darwin",
"x86_64-pc-windows-msvc",
]
llvm_assertions_default = ("SERVO_RUSTC_LLVM_ASSERTIONS" in os.environ
or host_triple() not in triples_with_rustc_alt_builds)
self.config.setdefault("build", {})
self.config["build"].setdefault("android", False)
self.config["build"].setdefault("mode", "")
self.config["build"].setdefault("llvm-assertions", llvm_assertions_default)
self.config["build"].setdefault("debug-mozjs", False)
self.config["build"].setdefault("ccache", "")
self.config["build"].setdefault("rustflags", "")
@ -315,8 +305,6 @@ class CommandBase(object):
def rust_install_dir(self):
if self._use_stable_rust:
return self.rust_stable_version()
elif not self.config["build"]["llvm-assertions"]:
return self.rust_nightly_date() + "-alt"
else:
return self.rust_nightly_date()

Просмотреть файл

@ -1 +1 @@
nightly-2017-11-09
nightly-2017-11-14

Просмотреть файл

@ -39,9 +39,6 @@ rustc-with-gold = true
# Defaults to prompting before building
#mode = "dev"
# Whether to enable LLVM assertions in rustc.
#llvm-assertions = false
# Set "android = true" or use `mach build --android` to build the Android app.
android = false

Просмотреть файл

@ -20,7 +20,6 @@
const video = document.getElementById("video");
const statusOverlay = getAnonElementWithinVideoByAttribute(video, "anonid", "statusOverlay");
const statusIcon = getAnonElementWithinVideoByAttribute(video, "anonid", "statusIcon");
const clickToPlay = getAnonElementWithinVideoByAttribute(video, "anonid", "clickToPlay");
add_task(async function setup() {
await new Promise(resolve => window.addEventListener("load", resolve, {once: true}));
@ -46,7 +45,6 @@
video.addEventListener("error", () => SimpleTest.executeSoon(resolve));
});
ok(clickToPlay.hasAttribute("hidden"), `click to play button should hide`);
ok(!statusOverlay.hidden, `statusOverlay should show when ${errorType}`);
is(statusOverlay.getAttribute("error"), errorType, `statusOverlay should have correct error state: ${errorType}`);
is(statusIcon.getAttribute("type"), "error", `should show error icon when ${errorType}`);

Просмотреть файл

@ -1740,8 +1740,7 @@
(clickToPlayScaledSize + this.controlBarMinHeight / 2 >= videoHeight / 2 )) {
this.clickToPlay.hideByAdjustment = true;
} else {
if (this.clickToPlay.hidden && !this.video.played.length &&
this.video.paused && this.clickToPlay.hideByAdjustment) {
if (this.clickToPlay.hidden && !this.video.played.length && this.video.paused) {
this.clickToPlay.hideByAdjustment = false;
}
this.clickToPlay.style.width = `${clickToPlayScaledSize}px`;

Просмотреть файл

@ -438,7 +438,8 @@ audio > xul|videocontrols {
transition-duration: 300ms;
transition-delay: 750ms;
}
.statusOverlay[fadeout] {
.statusOverlay[fadeout],
.statusOverlay[error] + .controlsOverlay > .controlsSpacerStack {
opacity: 0;
}

Просмотреть файл

@ -70,7 +70,6 @@ if CONFIG['NS_PRINTING']:
UNIFIED_SOURCES += [
'nsCUPSShim.cpp',
'nsDeviceContextSpecG.cpp',
'nsPaperPS.cpp',
'nsPrintDialogGTK.cpp',
'nsPrintOptionsGTK.cpp',
'nsPrintSettingsGTK.cpp',

Просмотреть файл

@ -19,7 +19,6 @@
#include "nsThreadUtils.h"
#include "nsPSPrinters.h"
#include "nsPaperPS.h" /* Paper size list */
#include "nsPrintSettingsGTK.h"

Просмотреть файл

@ -1,43 +0,0 @@
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* ex: set tabstop=8 softtabstop=4 shiftwidth=4 expandtab: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/ArrayUtils.h"
#include "nsPaperPS.h"
#include "plstr.h"
#include "nsCoord.h"
#include "nsMemory.h"
using namespace mozilla;
const nsPaperSizePS_ nsPaperSizePS::mList[] =
{
#define SIZE_MM(x) (x)
#define SIZE_INCH(x) ((x) * MM_PER_INCH_FLOAT)
{ "A5", SIZE_MM(148), SIZE_MM(210), true },
{ "A4", SIZE_MM(210), SIZE_MM(297), true },
{ "A3", SIZE_MM(297), SIZE_MM(420), true },
{ "Letter", SIZE_INCH(8.5), SIZE_INCH(11), false },
{ "Legal", SIZE_INCH(8.5), SIZE_INCH(14), false },
{ "Tabloid", SIZE_INCH(11), SIZE_INCH(17), false },
{ "Executive", SIZE_INCH(7.5), SIZE_INCH(10), false },
#undef SIZE_INCH
#undef SIZE_MM
};
const unsigned int nsPaperSizePS::mCount = ArrayLength(mList);
bool
nsPaperSizePS::Find(const char *aName)
{
for (int i = mCount; i--; ) {
if (!PL_strcasecmp(aName, mList[i].name)) {
mCurrent = i;
return true;
}
}
return false;
}

Просмотреть файл

@ -1,93 +0,0 @@
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* ex: set tabstop=8 softtabstop=4 shiftwidth=4 expandtab: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef _PAPERPS_H_
#define _PAPERPS_H_
#include "nsDebug.h"
struct nsPaperSizePS_ {
const char *name;
float width_mm;
float height_mm;
bool isMetric; // Present to the user in metric, if possible
};
class nsPaperSizePS {
public:
/** ---------------------------------------------------
* Constructor
*/
nsPaperSizePS() { mCurrent = 0; }
/** ---------------------------------------------------
* @return true if the cursor points past the last item.
*/
bool AtEnd() { return mCurrent >= mCount; }
/** ---------------------------------------------------
* Position the cursor at the beginning of the paper size list.
* @return VOID
*/
void First() { mCurrent = 0; }
/** ---------------------------------------------------
* Advance the cursor to the next item.
* @return VOID
*/
void Next() {
NS_ASSERTION(!AtEnd(), "Invalid current item");
mCurrent++;
}
/** ---------------------------------------------------
* Point the cursor to the entry with the given paper name.
* @return true if pointing to a valid entry.
*/
bool Find(const char *aName);
/** ---------------------------------------------------
* @return a pointer to the name of the current paper size
*/
const char *Name() {
NS_PRECONDITION(!AtEnd(), "Invalid current item");
return mList[mCurrent].name;
}
/** ---------------------------------------------------
* @return the width of the page in millimeters
*/
float Width_mm() {
NS_PRECONDITION(!AtEnd(), "Invalid current item");
return mList[mCurrent].width_mm;
}
/** ---------------------------------------------------
* @return the height of the page in millimeters
*/
float Height_mm() {
NS_PRECONDITION(!AtEnd(), "Invalid current item");
return mList[mCurrent].height_mm;
}
/** ---------------------------------------------------
* @return true if the paper should be presented to
* the user in metric units.
*/
bool IsMetric() {
NS_PRECONDITION(!AtEnd(), "Invalid current item");
return mList[mCurrent].isMetric;
}
private:
unsigned int mCurrent;
static const nsPaperSizePS_ mList[];
static const unsigned int mCount;
};
#endif

Просмотреть файл

@ -126,7 +126,7 @@ ca_context_get_default()
}
nsAutoString wbrand;
WidgetUtils::GetBrandShortName(wbrand);
mozilla::widget::WidgetUtils::GetBrandShortName(wbrand);
ca_context_change_props(ctx, "application.name",
NS_ConvertUTF16toUTF8(wbrand).get(),
nullptr);
@ -225,7 +225,7 @@ nsSound::GetInstance()
if (!sInstance) {
if (gfxPlatform::IsHeadless()) {
sInstance = new widget::HeadlessSound();
sInstance = new mozilla::widget::HeadlessSound();
} else {
sInstance = new nsSound();
}