updater: add 'ignoreDigest' to force-update on manage page
* saveStyle: retain only known properties in sections[] and normalize their order * remove styleDigest on import * shorten detailed status names in updater * don't autohide update status message
This commit is contained in:
		
							parent
							
								
									32ae088c03
								
							
						
					
					
						commit
						7677f0dece
					
				| 
						 | 
					@ -323,7 +323,7 @@
 | 
				
			||||||
    "description": "Checkbox to show only locally edited styles"
 | 
					    "description": "Checkbox to show only locally edited styles"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "manageOnlyUpdates": {
 | 
					  "manageOnlyUpdates": {
 | 
				
			||||||
    "message": "Only with updates",
 | 
					    "message": "Only with updates or problems",
 | 
				
			||||||
    "description": "Checkbox to show only styles that have updates after check-all-styles-for-updates was performed"
 | 
					    "description": "Checkbox to show only styles that have updates after check-all-styles-for-updates was performed"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "manageNewUI": {
 | 
					  "manageNewUI": {
 | 
				
			||||||
| 
						 | 
					@ -592,7 +592,7 @@
 | 
				
			||||||
    "description": "Text that displays when an update check skipped updating the style to avoid losing possible local modifications"
 | 
					    "description": "Text that displays when an update check skipped updating the style to avoid losing possible local modifications"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "updateCheckManualUpdateHint": {
 | 
					  "updateCheckManualUpdateHint": {
 | 
				
			||||||
    "message": "Do a one-time manual update on its userstyles.org page (your edits will be lost)",
 | 
					    "message": "To force an update (and lose your edits) update each style individually.",
 | 
				
			||||||
    "description": "Additional text displayed when an update check skipped updating the style to avoid losing local modifications"
 | 
					    "description": "Additional text displayed when an update check skipped updating the style to avoid losing local modifications"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "updateCheckSucceededNoUpdate": {
 | 
					  "updateCheckSucceededNoUpdate": {
 | 
				
			||||||
| 
						 | 
					@ -600,7 +600,11 @@
 | 
				
			||||||
    "description": "Text that displays when an update check completed and no update is available"
 | 
					    "description": "Text that displays when an update check completed and no update is available"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "updateAllCheckSucceededNoUpdate": {
 | 
					  "updateAllCheckSucceededNoUpdate": {
 | 
				
			||||||
    "message": "All styles are up to date.",
 | 
					    "message": "No updates found.",
 | 
				
			||||||
 | 
					    "description": "Text that displays when an update all check completed and no updates are available"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  "updateAllCheckSucceededSomeEdited": {
 | 
				
			||||||
 | 
					    "message": "Some updatable styles weren't checked to avoid losing possible local edits.",
 | 
				
			||||||
    "description": "Text that displays when an update all check completed and no updates are available"
 | 
					    "description": "Text that displays when an update all check completed and no updates are available"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "updateCompleted": {
 | 
					  "updateCompleted": {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -294,7 +294,7 @@ summary {
 | 
				
			||||||
  cursor: pointer;
 | 
					  cursor: pointer;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.update-problem .check-update svg {
 | 
					.newUI .update-problem .check-update svg {
 | 
				
			||||||
  fill: #ef6969;
 | 
					  fill: #ef6969;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -500,6 +500,10 @@ input[id^="manage.newUI"] {
 | 
				
			||||||
  opacity: .35;
 | 
					  opacity: .35;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#update-all-no-updates[data-skipped-edited="true"]:after {
 | 
				
			||||||
 | 
					  content: " __MSG_updateAllCheckSucceededSomeEdited__ __MSG_updateCheckManualUpdateHint__";
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* highlight updated/added styles */
 | 
					/* highlight updated/added styles */
 | 
				
			||||||
.highlight {
 | 
					.highlight {
 | 
				
			||||||
  animation: highlight 10s cubic-bezier(0,.82,.47,.98);
 | 
					  animation: highlight 10s cubic-bezier(0,.82,.47,.98);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										16
									
								
								manage.html
									
									
									
									
									
								
							
							
						
						
									
										16
									
								
								manage.html
									
									
									
									
									
								
							| 
						 | 
					@ -135,18 +135,26 @@
 | 
				
			||||||
  <fieldset>
 | 
					  <fieldset>
 | 
				
			||||||
    <legend id="filters" i18n-text="manageFilters"></legend>
 | 
					    <legend id="filters" i18n-text="manageFilters"></legend>
 | 
				
			||||||
    <label>
 | 
					    <label>
 | 
				
			||||||
      <input id="manage.onlyEnabled" type="checkbox" data-filter=".disabled">
 | 
					      <input id="manage.onlyEnabled" type="checkbox"
 | 
				
			||||||
 | 
					             data-filter=".enabled"
 | 
				
			||||||
 | 
					             data-filter-hide=".disabled">
 | 
				
			||||||
      <span i18n-text="manageOnlyEnabled"></span>
 | 
					      <span i18n-text="manageOnlyEnabled"></span>
 | 
				
			||||||
    </label>
 | 
					    </label>
 | 
				
			||||||
    <label>
 | 
					    <label>
 | 
				
			||||||
      <input id="manage.onlyEdited" type="checkbox" data-filter=".updatable">
 | 
					      <input id="manage.onlyEdited" type="checkbox"
 | 
				
			||||||
 | 
					             data-filter=":not(.updatable)"
 | 
				
			||||||
 | 
					             data-filter-hide=".updatable">
 | 
				
			||||||
      <span i18n-text="manageOnlyEdited"></span>
 | 
					      <span i18n-text="manageOnlyEdited"></span>
 | 
				
			||||||
    </label>
 | 
					    </label>
 | 
				
			||||||
    <label id="onlyUpdates" class="hidden">
 | 
					    <label id="onlyUpdates" class="hidden">
 | 
				
			||||||
      <input type="checkbox" data-filter=":not(.can-update)">
 | 
					      <input type="checkbox"
 | 
				
			||||||
 | 
					             data-filter=".can-update, .update-problem"
 | 
				
			||||||
 | 
					             data-filter-hide=":not(.updatable), .update-done, .no-update:not(.update-problem)">
 | 
				
			||||||
      <span i18n-text="manageOnlyUpdates"></span>
 | 
					      <span i18n-text="manageOnlyUpdates"></span>
 | 
				
			||||||
    </label>
 | 
					    </label>
 | 
				
			||||||
    <input id="search" type="search" i18n-placeholder="searchStyles" data-filter=".not-matching">
 | 
					    <input id="search" type="search" i18n-placeholder="searchStyles"
 | 
				
			||||||
 | 
					           data-filter=":not(.not-matching)"
 | 
				
			||||||
 | 
					           data-filter-hide=".not-matching">
 | 
				
			||||||
  </fieldset>
 | 
					  </fieldset>
 | 
				
			||||||
  <p>
 | 
					  <p>
 | 
				
			||||||
    <button id="check-all-updates" i18n-text="checkAllUpdates"><span id="update-progress"></span></button>
 | 
					    <button id="check-all-updates" i18n-text="checkAllUpdates"><span id="update-progress"></span></button>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										81
									
								
								manage.js
									
									
									
									
									
								
							
							
						
						
									
										81
									
								
								manage.js
									
									
									
									
									
								
							| 
						 | 
					@ -365,10 +365,15 @@ Object.assign(handleEvent, {
 | 
				
			||||||
      el.lastValue = value;
 | 
					      el.lastValue = value;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    const enabledFilters = $$('#header [data-filter]').filter(el => getValue(el));
 | 
					    const enabledFilters = $$('#header [data-filter]').filter(el => getValue(el));
 | 
				
			||||||
 | 
					    const buildFilter = hide =>
 | 
				
			||||||
 | 
					      [...enabledFilters.map(el =>
 | 
				
			||||||
 | 
					        el.dataset[hide ? 'filterHide' : 'filter']
 | 
				
			||||||
 | 
					          .split(/,\s*/)
 | 
				
			||||||
 | 
					          .map(s => '.entry' + (hide ? '' : '.hidden') + s))
 | 
				
			||||||
 | 
					      ].join(',');
 | 
				
			||||||
    Object.assign(filtersSelector, {
 | 
					    Object.assign(filtersSelector, {
 | 
				
			||||||
      hide: enabledFilters.map(el => '.entry:not(.hidden)' + el.dataset.filter).join(','),
 | 
					      hide: buildFilter(true),
 | 
				
			||||||
      unhide: '.entry.hidden' + enabledFilters.map(el =>
 | 
					      unhide: buildFilter(false),
 | 
				
			||||||
        (':not(' + el.dataset.filter + ')').replace(/^:not\(:not\((.+?)\)\)$/, '$1')).join(''),
 | 
					 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
    reapplyFilter();
 | 
					    reapplyFilter();
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
| 
						 | 
					@ -505,12 +510,13 @@ function checkUpdateAll() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  let total = 0;
 | 
					  let total = 0;
 | 
				
			||||||
  let checked = 0;
 | 
					  let checked = 0;
 | 
				
			||||||
 | 
					  let skippedEdited = 0;
 | 
				
			||||||
  let updated = 0;
 | 
					  let updated = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  $$('.updatable:not(.can-update)').map(el => checkUpdate(el, {single: false}));
 | 
					  $$('.updatable:not(.can-update):not(.update-problem)').map(el => checkUpdate(el, {single: false}));
 | 
				
			||||||
  BG.updater.checkAllStyles(observe, {save: false}).then(done);
 | 
					  BG.updater.checkAllStyles({observer, save: false});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  function observe(state, value, details) {
 | 
					  function observer(state, value, details) {
 | 
				
			||||||
    switch (state) {
 | 
					    switch (state) {
 | 
				
			||||||
      case BG.updater.COUNT:
 | 
					      case BG.updater.COUNT:
 | 
				
			||||||
        total = value;
 | 
					        total = value;
 | 
				
			||||||
| 
						 | 
					@ -524,37 +530,41 @@ function checkUpdateAll() {
 | 
				
			||||||
        // fallthrough
 | 
					        // fallthrough
 | 
				
			||||||
      case BG.updater.SKIPPED:
 | 
					      case BG.updater.SKIPPED:
 | 
				
			||||||
        checked++;
 | 
					        checked++;
 | 
				
			||||||
 | 
					        if (details == BG.updater.EDITED || details == BG.updater.MAYBE_EDITED) {
 | 
				
			||||||
 | 
					          skippedEdited++;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
        reportUpdateState(state, value, details);
 | 
					        reportUpdateState(state, value, details);
 | 
				
			||||||
        break;
 | 
					        break;
 | 
				
			||||||
 | 
					      case BG.updater.DONE:
 | 
				
			||||||
 | 
					        $('#check-all-updates').disabled = false;
 | 
				
			||||||
 | 
					        $('#apply-all-updates').disabled = false;
 | 
				
			||||||
 | 
					        renderUpdatesOnlyFilter({check: updated + skippedEdited > 0});
 | 
				
			||||||
 | 
					        if (!updated) {
 | 
				
			||||||
 | 
					          $('#update-all-no-updates').dataset.skippedEdited = skippedEdited > 0;
 | 
				
			||||||
 | 
					          $('#update-all-no-updates').classList.remove('hidden');
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    const progress = $('#update-progress');
 | 
					    const progress = $('#update-progress');
 | 
				
			||||||
    const maxWidth = progress.parentElement.clientWidth;
 | 
					    const maxWidth = progress.parentElement.clientWidth;
 | 
				
			||||||
    progress.style.width = Math.round(checked / total * maxWidth) + 'px';
 | 
					    progress.style.width = Math.round(checked / total * maxWidth) + 'px';
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					 | 
				
			||||||
  function done() {
 | 
					 | 
				
			||||||
    $('#check-all-updates').disabled = false;
 | 
					 | 
				
			||||||
    $('#apply-all-updates').disabled = false;
 | 
					 | 
				
			||||||
    renderUpdatesOnlyFilter({check: updated > 0});
 | 
					 | 
				
			||||||
    if (!updated) {
 | 
					 | 
				
			||||||
      $('#update-all-no-updates').classList.remove('hidden');
 | 
					 | 
				
			||||||
      setTimeout(() => {
 | 
					 | 
				
			||||||
        $('#update-all-no-updates').classList.add('hidden');
 | 
					 | 
				
			||||||
      }, 10e3);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function checkUpdate(element, {single = true} = {}) {
 | 
					function checkUpdate(entry, {single = true} = {}) {
 | 
				
			||||||
  $('.update-note', element).textContent = t('checkingForUpdate');
 | 
					  $('.update-note', entry).textContent = t('checkingForUpdate');
 | 
				
			||||||
  $('.check-update', element).title = '';
 | 
					  $('.check-update', entry).title = '';
 | 
				
			||||||
  element.classList.remove('checking-update', 'no-update', 'update-problem');
 | 
					 | 
				
			||||||
  element.classList.add('checking-update');
 | 
					 | 
				
			||||||
  if (single) {
 | 
					  if (single) {
 | 
				
			||||||
    const style = BG.cachedStyles.byId.get(element.styleId);
 | 
					    BG.updater.checkStyle({
 | 
				
			||||||
    BG.updater.checkStyle(style, reportUpdateState, {save: false});
 | 
					      save: false,
 | 
				
			||||||
 | 
					      ignoreDigest: entry.classList.contains('update-problem'),
 | 
				
			||||||
 | 
					      style: BG.cachedStyles.byId.get(entry.styleId),
 | 
				
			||||||
 | 
					      observer: reportUpdateState,
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					  entry.classList.remove('checking-update', 'no-update', 'update-problem');
 | 
				
			||||||
 | 
					  entry.classList.add('checking-update');
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -569,18 +579,19 @@ function reportUpdateState(state, style, details) {
 | 
				
			||||||
      $('#onlyUpdates').classList.remove('hidden');
 | 
					      $('#onlyUpdates').classList.remove('hidden');
 | 
				
			||||||
      break;
 | 
					      break;
 | 
				
			||||||
    case BG.updater.SKIPPED: {
 | 
					    case BG.updater.SKIPPED: {
 | 
				
			||||||
 | 
					      if (entry.classList.contains('can-update')) {
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
      if (!details) {
 | 
					      if (!details) {
 | 
				
			||||||
        details = t('updateCheckFailServerUnreachable');
 | 
					        details = t('updateCheckFailServerUnreachable');
 | 
				
			||||||
      } else if (typeof details == 'number') {
 | 
					      } else if (typeof details == 'number') {
 | 
				
			||||||
        details = t('updateCheckFailBadResponseCode', [details]);
 | 
					        details = t('updateCheckFailBadResponseCode', [details]);
 | 
				
			||||||
      } else if (details == BG.updater.SKIPPED_EDITED) {
 | 
					      } else if (details == BG.updater.EDITED) {
 | 
				
			||||||
        details = t('updateCheckSkippedLocallyEdited') + '\n' + t('updateCheckManualUpdateHint');
 | 
					        details = t('updateCheckSkippedLocallyEdited') + '\n' + t('updateCheckManualUpdateHint');
 | 
				
			||||||
      } else if (details == BG.updater.SKIPPED_MAYBE_EDITED) {
 | 
					      } else if (details == BG.updater.MAYBE_EDITED) {
 | 
				
			||||||
        details = t('updateCheckSkippedMaybeLocallyEdited') + '\n' + t('updateCheckManualUpdateHint');
 | 
					        details = t('updateCheckSkippedMaybeLocallyEdited') + '\n' + t('updateCheckManualUpdateHint');
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      const same =
 | 
					      const same = details == BG.updater.SAME_MD5 || details == BG.updater.SAME_CODE;
 | 
				
			||||||
        details == BG.updater.SKIPPED_SAME_MD5 ||
 | 
					 | 
				
			||||||
        details == BG.updater.SKIPPED_SAME_CODE;
 | 
					 | 
				
			||||||
      const message = same ? t('updateCheckSucceededNoUpdate') : details;
 | 
					      const message = same ? t('updateCheckSucceededNoUpdate') : details;
 | 
				
			||||||
      entry.classList.add('no-update');
 | 
					      entry.classList.add('no-update');
 | 
				
			||||||
      entry.classList.toggle('update-problem', !same);
 | 
					      entry.classList.toggle('update-problem', !same);
 | 
				
			||||||
| 
						 | 
					@ -588,7 +599,7 @@ function reportUpdateState(state, style, details) {
 | 
				
			||||||
      $('.check-update', entry).title = newUI.enabled ? message : '';
 | 
					      $('.check-update', entry).title = newUI.enabled ? message : '';
 | 
				
			||||||
      if (!$('#check-all-updates').disabled) {
 | 
					      if (!$('#check-all-updates').disabled) {
 | 
				
			||||||
        // this is a single update job so we can decide whether to hide the filter
 | 
					        // this is a single update job so we can decide whether to hide the filter
 | 
				
			||||||
        $('#onlyUpdates').classList.toggle('hidden', !$('.can-update'));
 | 
					        renderUpdatesOnlyFilter({show: $('.can-update, .update-problem')});
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
| 
						 | 
					@ -600,10 +611,10 @@ function reportUpdateState(state, style, details) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function renderUpdatesOnlyFilter({show, check} = {}) {
 | 
					function renderUpdatesOnlyFilter({show, check} = {}) {
 | 
				
			||||||
  const numUpdatable = $$('.can-update').length;
 | 
					  const numUpdatable = $$('.can-update').length;
 | 
				
			||||||
  const canUpdate = numUpdatable > 0;
 | 
					  const mightUpdate = numUpdatable > 0 || $('.update-problem');
 | 
				
			||||||
  const checkbox = $('#onlyUpdates input');
 | 
					  const checkbox = $('#onlyUpdates input');
 | 
				
			||||||
  show = show !== undefined ? show : canUpdate;
 | 
					  show = show !== undefined ? show : mightUpdate;
 | 
				
			||||||
  check = check !== undefined ? show && check : checkbox.checked && canUpdate;
 | 
					  check = check !== undefined ? show && check : checkbox.checked && mightUpdate;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  $('#onlyUpdates').classList.toggle('hidden', !show);
 | 
					  $('#onlyUpdates').classList.toggle('hidden', !show);
 | 
				
			||||||
  checkbox.checked = check;
 | 
					  checkbox.checked = check;
 | 
				
			||||||
| 
						 | 
					@ -611,7 +622,7 @@ function renderUpdatesOnlyFilter({show, check} = {}) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const btnApply = $('#apply-all-updates');
 | 
					  const btnApply = $('#apply-all-updates');
 | 
				
			||||||
  if (!btnApply.matches('.hidden')) {
 | 
					  if (!btnApply.matches('.hidden')) {
 | 
				
			||||||
    if (canUpdate) {
 | 
					    if (numUpdatable > 0) {
 | 
				
			||||||
      btnApply.dataset.value = numUpdatable;
 | 
					      btnApply.dataset.value = numUpdatable;
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
      btnApply.classList.add('hidden');
 | 
					      btnApply.classList.add('hidden');
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -41,16 +41,14 @@ function checkUpdates() {
 | 
				
			||||||
  let total = 0;
 | 
					  let total = 0;
 | 
				
			||||||
  let checked = 0;
 | 
					  let checked = 0;
 | 
				
			||||||
  let updated = 0;
 | 
					  let updated = 0;
 | 
				
			||||||
  const installed = $('#updates-installed');
 | 
					  const maxWidth = $('#update-progress').parentElement.clientWidth;
 | 
				
			||||||
  const progress = $('#update-progress');
 | 
					  BG.updater.checkAllStyles({observer});
 | 
				
			||||||
  const maxWidth = progress.parentElement.clientWidth;
 | 
					
 | 
				
			||||||
  progress.style.width = 0;
 | 
					  function observer(state, value) {
 | 
				
			||||||
  installed.dataset.value = '';
 | 
					 | 
				
			||||||
  document.body.classList.add('update-in-progress');
 | 
					 | 
				
			||||||
  BG.updater.checkAllStyles((state, value) => {
 | 
					 | 
				
			||||||
    switch (state) {
 | 
					    switch (state) {
 | 
				
			||||||
      case BG.updater.COUNT:
 | 
					      case BG.updater.COUNT:
 | 
				
			||||||
        total = value;
 | 
					        total = value;
 | 
				
			||||||
 | 
					        document.body.classList.add('update-in-progress');
 | 
				
			||||||
        break;
 | 
					        break;
 | 
				
			||||||
      case BG.updater.UPDATED:
 | 
					      case BG.updater.UPDATED:
 | 
				
			||||||
        updated++;
 | 
					        updated++;
 | 
				
			||||||
| 
						 | 
					@ -58,10 +56,11 @@ function checkUpdates() {
 | 
				
			||||||
      case BG.updater.SKIPPED:
 | 
					      case BG.updater.SKIPPED:
 | 
				
			||||||
        checked++;
 | 
					        checked++;
 | 
				
			||||||
        break;
 | 
					        break;
 | 
				
			||||||
    }
 | 
					      case BG.updater.DONE:
 | 
				
			||||||
    progress.style.width = Math.round(checked / total * maxWidth) + 'px';
 | 
					 | 
				
			||||||
    installed.dataset.value = updated || '';
 | 
					 | 
				
			||||||
  }).then(() => {
 | 
					 | 
				
			||||||
        document.body.classList.remove('update-in-progress');
 | 
					        document.body.classList.remove('update-in-progress');
 | 
				
			||||||
  });
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    $('#update-progress').style.width = Math.round(checked / total * maxWidth) + 'px';
 | 
				
			||||||
 | 
					    $('#updates-installed').dataset.value = updated || '';
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										56
									
								
								storage.js
									
									
									
									
									
								
							
							
						
						
									
										56
									
								
								storage.js
									
									
									
									
									
								
							| 
						 | 
					@ -218,7 +218,7 @@ function saveStyle(style) {
 | 
				
			||||||
      const tx = db.transaction(['styles'], 'readwrite');
 | 
					      const tx = db.transaction(['styles'], 'readwrite');
 | 
				
			||||||
      const os = tx.objectStore('styles');
 | 
					      const os = tx.objectStore('styles');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      const id = style.id !== undefined && style.id !== null ? Number(style.id) : null;
 | 
					      const id = style.id == '0' ? 0 : Number(style.id) || null;
 | 
				
			||||||
      const reason = style.reason;
 | 
					      const reason = style.reason;
 | 
				
			||||||
      const notify = style.notify !== false;
 | 
					      const notify = style.notify !== false;
 | 
				
			||||||
      delete style.method;
 | 
					      delete style.method;
 | 
				
			||||||
| 
						 | 
					@ -227,15 +227,16 @@ function saveStyle(style) {
 | 
				
			||||||
      if (!style.name) {
 | 
					      if (!style.name) {
 | 
				
			||||||
        delete style.name;
 | 
					        delete style.name;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					      let existed, codeIsUpdated;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if (id !== null) {
 | 
					      if (id !== null) {
 | 
				
			||||||
        // Update or create
 | 
					        // Update or create
 | 
				
			||||||
        style.id = id;
 | 
					        style.id = id;
 | 
				
			||||||
        os.get(id).onsuccess = eventGet => {
 | 
					        os.get(id).onsuccess = eventGet => {
 | 
				
			||||||
          const existed = Boolean(eventGet.target.result);
 | 
					          const oldStyle = eventGet.target.result;
 | 
				
			||||||
          const oldStyle = Object.assign({}, eventGet.target.result);
 | 
					          existed = Boolean(oldStyle);
 | 
				
			||||||
          const codeIsUpdated = 'sections' in style && !styleSectionsEqual(style, oldStyle);
 | 
					          codeIsUpdated = !existed || style.sections && !styleSectionsEqual(style, oldStyle);
 | 
				
			||||||
          write(Object.assign(oldStyle, style), {reason, existed, codeIsUpdated});
 | 
					          write(Object.assign({}, oldStyle, style));
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
      } else {
 | 
					      } else {
 | 
				
			||||||
        // Create
 | 
					        // Create
 | 
				
			||||||
| 
						 | 
					@ -247,18 +248,11 @@ function saveStyle(style) {
 | 
				
			||||||
          md5Url: null,
 | 
					          md5Url: null,
 | 
				
			||||||
          url: null,
 | 
					          url: null,
 | 
				
			||||||
          originalMd5: null,
 | 
					          originalMd5: null,
 | 
				
			||||||
        }, style), {reason});
 | 
					        }, style));
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      function write(style, {reason, existed, codeIsUpdated} = {}) {
 | 
					      function write(style) {
 | 
				
			||||||
        style.sections = (style.sections || []).map(section =>
 | 
					        style.sections = normalizeStyleSections(style);
 | 
				
			||||||
          Object.assign({
 | 
					 | 
				
			||||||
            urls: [],
 | 
					 | 
				
			||||||
            urlPrefixes: [],
 | 
					 | 
				
			||||||
            domains: [],
 | 
					 | 
				
			||||||
            regexps: [],
 | 
					 | 
				
			||||||
          }, section)
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
        os.put(style).onsuccess = event => {
 | 
					        os.put(style).onsuccess = event => {
 | 
				
			||||||
          style.id = style.id || event.target.result;
 | 
					          style.id = style.id || event.target.result;
 | 
				
			||||||
          invalidateCache(existed ? {updated: style} : {added: style});
 | 
					          invalidateCache(existed ? {updated: style} : {added: style});
 | 
				
			||||||
| 
						 | 
					@ -271,6 +265,8 @@ function saveStyle(style) {
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
          if (reason == 'update') {
 | 
					          if (reason == 'update') {
 | 
				
			||||||
            updateStyleDigest(style);
 | 
					            updateStyleDigest(style);
 | 
				
			||||||
 | 
					          } else if (reason == 'import') {
 | 
				
			||||||
 | 
					            chrome.storage.local.remove(DIGEST_KEY_PREFIX + style.id, ignoreChromeError);
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
          resolve(style);
 | 
					          resolve(style);
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
| 
						 | 
					@ -308,10 +304,14 @@ function getApplicableSections({style, matchUrl, strictRegexp = true, stopOnFirs
 | 
				
			||||||
  for (const section of style.sections) {
 | 
					  for (const section of style.sections) {
 | 
				
			||||||
    const {urls, domains, urlPrefixes, regexps, code} = section;
 | 
					    const {urls, domains, urlPrefixes, regexps, code} = section;
 | 
				
			||||||
    if ((!urls.length && !urlPrefixes.length && !domains.length && !regexps.length
 | 
					    if ((!urls.length && !urlPrefixes.length && !domains.length && !regexps.length
 | 
				
			||||||
      || urls.length && urls.indexOf(matchUrl) >= 0
 | 
					      || urls.length
 | 
				
			||||||
      || urlPrefixes.length && arraySomeIsPrefix(urlPrefixes, matchUrl)
 | 
					        && urls.indexOf(matchUrl) >= 0
 | 
				
			||||||
      || domains.length && arraySomeIn(cachedStyles.urlDomains.get(matchUrl) || getDomains(matchUrl), domains)
 | 
					      || urlPrefixes.length
 | 
				
			||||||
      || regexps.length && arraySomeMatches(regexps, matchUrl, strictRegexp)
 | 
					        && arraySomeIsPrefix(urlPrefixes, matchUrl)
 | 
				
			||||||
 | 
					      || domains.length
 | 
				
			||||||
 | 
					        && arraySomeIn(cachedStyles.urlDomains.get(matchUrl) || getDomains(matchUrl), domains)
 | 
				
			||||||
 | 
					      || regexps.length
 | 
				
			||||||
 | 
					        && arraySomeMatches(regexps, matchUrl, strictRegexp)
 | 
				
			||||||
    ) && !styleCodeEmpty(code)) {
 | 
					    ) && !styleCodeEmpty(code)) {
 | 
				
			||||||
      sections.push(section);
 | 
					      sections.push(section);
 | 
				
			||||||
      if (stopOnFirst) {
 | 
					      if (stopOnFirst) {
 | 
				
			||||||
| 
						 | 
					@ -534,6 +534,18 @@ function getDomains(url) {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function normalizeStyleSections({sections}) {
 | 
				
			||||||
 | 
					  // retain known properties in an arbitrarily predefined order
 | 
				
			||||||
 | 
					  return (sections || []).map(section => ({
 | 
				
			||||||
 | 
					    code: section.code || '',
 | 
				
			||||||
 | 
					    urls: section.urls || [],
 | 
				
			||||||
 | 
					    urlPrefixes: section.urlPrefixes || [],
 | 
				
			||||||
 | 
					    domains: section.domains || [],
 | 
				
			||||||
 | 
					    regexps: section.regexps || [],
 | 
				
			||||||
 | 
					  }));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function getStyleDigests(style) {
 | 
					function getStyleDigests(style) {
 | 
				
			||||||
  return Promise.all([
 | 
					  return Promise.all([
 | 
				
			||||||
    chromeLocal.getValue(DIGEST_KEY_PREFIX + style.id),
 | 
					    chromeLocal.getValue(DIGEST_KEY_PREFIX + style.id),
 | 
				
			||||||
| 
						 | 
					@ -548,9 +560,11 @@ function updateStyleDigest(style) {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function calcStyleDigest({sections}) {
 | 
					function calcStyleDigest(style) {
 | 
				
			||||||
  const text = new TextEncoder('utf-8').encode(JSON.stringify(sections));
 | 
					  const jsonString = JSON.stringify(normalizeStyleSections(style));
 | 
				
			||||||
 | 
					  const text = new TextEncoder('utf-8').encode(jsonString);
 | 
				
			||||||
  return crypto.subtle.digest('SHA-1', text).then(hex);
 | 
					  return crypto.subtle.digest('SHA-1', text).then(hex);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  function hex(buffer) {
 | 
					  function hex(buffer) {
 | 
				
			||||||
    const parts = [];
 | 
					    const parts = [];
 | 
				
			||||||
    const PAD8 = '00000000';
 | 
					    const PAD8 = '00000000';
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										65
									
								
								update.js
									
									
									
									
									
								
							
							
						
						
									
										65
									
								
								update.js
									
									
									
									
									
								
							| 
						 | 
					@ -1,4 +1,5 @@
 | 
				
			||||||
/* globals getStyles, saveStyle, styleSectionsEqual, getStyleDigests, updateStyleDigest */
 | 
					/* global getStyles, saveStyle, styleSectionsEqual */
 | 
				
			||||||
 | 
					/* global getStyleDigests, updateStyleDigest */
 | 
				
			||||||
'use strict';
 | 
					'use strict';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// eslint-disable-next-line no-var
 | 
					// eslint-disable-next-line no-var
 | 
				
			||||||
| 
						 | 
					@ -7,55 +8,71 @@ var updater = {
 | 
				
			||||||
  COUNT: 'count',
 | 
					  COUNT: 'count',
 | 
				
			||||||
  UPDATED: 'updated',
 | 
					  UPDATED: 'updated',
 | 
				
			||||||
  SKIPPED: 'skipped',
 | 
					  SKIPPED: 'skipped',
 | 
				
			||||||
  SKIPPED_EDITED: 'locally edited',
 | 
					 | 
				
			||||||
  SKIPPED_MAYBE_EDITED: 'maybe locally edited',
 | 
					 | 
				
			||||||
  SKIPPED_SAME_MD5: 'up-to-date: MD5 is unchanged',
 | 
					 | 
				
			||||||
  SKIPPED_SAME_CODE: 'up-to-date: code sections are unchanged',
 | 
					 | 
				
			||||||
  SKIPPED_ERROR_MD5: 'error: MD5 is invalid',
 | 
					 | 
				
			||||||
  SKIPPED_ERROR_JSON: 'error: JSON is invalid',
 | 
					 | 
				
			||||||
  DONE: 'done',
 | 
					  DONE: 'done',
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // details for SKIPPED status
 | 
				
			||||||
 | 
					  EDITED: 'locally edited',
 | 
				
			||||||
 | 
					  MAYBE_EDITED: 'maybe locally edited',
 | 
				
			||||||
 | 
					  SAME_MD5: 'up-to-date: MD5 is unchanged',
 | 
				
			||||||
 | 
					  SAME_CODE: 'up-to-date: code sections are unchanged',
 | 
				
			||||||
 | 
					  ERROR_MD5: 'error: MD5 is invalid',
 | 
				
			||||||
 | 
					  ERROR_JSON: 'error: JSON is invalid',
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  lastUpdateTime: parseInt(localStorage.lastUpdateTime) || Date.now(),
 | 
					  lastUpdateTime: parseInt(localStorage.lastUpdateTime) || Date.now(),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  checkAllStyles(observe = () => {}, {save = true} = {}) {
 | 
					  checkAllStyles({observer = () => {}, save = true, ignoreDigest} = {}) {
 | 
				
			||||||
    updater.resetInterval();
 | 
					    updater.resetInterval();
 | 
				
			||||||
    return new Promise(resolve => {
 | 
					    return new Promise(resolve => {
 | 
				
			||||||
      getStyles({}, styles => {
 | 
					      getStyles({}, styles => {
 | 
				
			||||||
        styles = styles.filter(style => style.updateUrl);
 | 
					        styles = styles.filter(style => style.updateUrl);
 | 
				
			||||||
        observe(updater.COUNT, styles.length);
 | 
					        observer(updater.COUNT, styles.length);
 | 
				
			||||||
        Promise.all(styles.map(style =>
 | 
					        Promise.all(styles.map(style =>
 | 
				
			||||||
          updater.checkStyle(style, observe, {save})
 | 
					          updater.checkStyle({style, observer, save, ignoreDigest})
 | 
				
			||||||
        )).then(() => {
 | 
					        )).then(() => {
 | 
				
			||||||
          observe(updater.DONE);
 | 
					          observer(updater.DONE);
 | 
				
			||||||
          resolve();
 | 
					          resolve();
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  checkStyle(style, observe = () => {}, {save = true} = {}) {
 | 
					  checkStyle({style, observer = () => {}, save = true, ignoreDigest}) {
 | 
				
			||||||
    let hasDigest;
 | 
					    let hasDigest;
 | 
				
			||||||
 | 
					    /*
 | 
				
			||||||
 | 
					    Original style digests are calculated in these cases:
 | 
				
			||||||
 | 
					    * style is installed or updated from server
 | 
				
			||||||
 | 
					    * style is checked for an update and its code is equal to the server code
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Update check proceeds in these cases:
 | 
				
			||||||
 | 
					    * style has the original digest and it's equal to the current digest
 | 
				
			||||||
 | 
					    * [ignoreDigest: true] style doesn't yet have the original digest but we ignore it
 | 
				
			||||||
 | 
					    * [ignoreDigest: none/false] style doesn't yet have the original digest
 | 
				
			||||||
 | 
					      so we compare the code to the server code and if it's the same we save the digest,
 | 
				
			||||||
 | 
					      otherwise we skip the style and report MAYBE_EDITED status
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    'ignoreDigest' option is set on the second manual individual update check on the manage page.
 | 
				
			||||||
 | 
					    */
 | 
				
			||||||
    return getStyleDigests(style)
 | 
					    return getStyleDigests(style)
 | 
				
			||||||
      .then(fetchMd5IfNotEdited)
 | 
					      .then(fetchMd5IfNotEdited)
 | 
				
			||||||
      .then(fetchCodeIfMd5Changed)
 | 
					      .then(fetchCodeIfMd5Changed)
 | 
				
			||||||
      .then(saveIfUpdated)
 | 
					      .then(saveIfUpdated)
 | 
				
			||||||
      .then(saved => observe(updater.UPDATED, saved))
 | 
					      .then(saved => observer(updater.UPDATED, saved))
 | 
				
			||||||
      .catch(err => observe(updater.SKIPPED, style, err));
 | 
					      .catch(err => observer(updater.SKIPPED, style, err));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    function fetchMd5IfNotEdited([originalDigest, current]) {
 | 
					    function fetchMd5IfNotEdited([originalDigest, current]) {
 | 
				
			||||||
      hasDigest = Boolean(originalDigest);
 | 
					      hasDigest = Boolean(originalDigest);
 | 
				
			||||||
      if (hasDigest && originalDigest != current) {
 | 
					      if (hasDigest && !ignoreDigest && originalDigest != current) {
 | 
				
			||||||
        return Promise.reject(updater.SKIPPED_EDITED);
 | 
					        return Promise.reject(updater.EDITED);
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      return download(style.md5Url);
 | 
					      return download(style.md5Url);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    function fetchCodeIfMd5Changed(md5) {
 | 
					    function fetchCodeIfMd5Changed(md5) {
 | 
				
			||||||
      if (!md5 || md5.length != 32) {
 | 
					      if (!md5 || md5.length != 32) {
 | 
				
			||||||
        return Promise.reject(updater.SKIPPED_ERROR_MD5);
 | 
					        return Promise.reject(updater.ERROR_MD5);
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      if (md5 == style.originalMd5 && hasDigest) {
 | 
					      if (md5 == style.originalMd5 && hasDigest) {
 | 
				
			||||||
        return Promise.reject(updater.SKIPPED_SAME_MD5);
 | 
					        return Promise.reject(updater.SAME_MD5);
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      return download(style.updateUrl);
 | 
					      return download(style.updateUrl);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					@ -63,16 +80,16 @@ var updater = {
 | 
				
			||||||
    function saveIfUpdated(text) {
 | 
					    function saveIfUpdated(text) {
 | 
				
			||||||
      const json = tryJSONparse(text);
 | 
					      const json = tryJSONparse(text);
 | 
				
			||||||
      if (!styleJSONseemsValid(json)) {
 | 
					      if (!styleJSONseemsValid(json)) {
 | 
				
			||||||
        return Promise.reject(updater.SKIPPED_ERROR_JSON);
 | 
					        return Promise.reject(updater.ERROR_JSON);
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      json.id = style.id;
 | 
					      json.id = style.id;
 | 
				
			||||||
      if (styleSectionsEqual(json, style)) {
 | 
					      if (styleSectionsEqual(json, style)) {
 | 
				
			||||||
        if (!hasDigest) {
 | 
					        // JSONs may have different order of items even if sections are effectively equal
 | 
				
			||||||
 | 
					        // so we'll update the digest anyway
 | 
				
			||||||
        updateStyleDigest(json);
 | 
					        updateStyleDigest(json);
 | 
				
			||||||
        }
 | 
					        return Promise.reject(updater.SAME_CODE);
 | 
				
			||||||
        return Promise.reject(updater.SKIPPED_SAME_CODE);
 | 
					      } else if (!hasDigest && !ignoreDigest) {
 | 
				
			||||||
      } else if (!hasDigest) {
 | 
					        return Promise.reject(updater.MAYBE_EDITED);
 | 
				
			||||||
        return Promise.reject(updater.SKIPPED_MAYBE_EDITED);
 | 
					 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      return !save ? json :
 | 
					      return !save ? json :
 | 
				
			||||||
        saveStyle(Object.assign(json, {
 | 
					        saveStyle(Object.assign(json, {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue
	
	Block a user