Commit 8fb22b27 authored by Robert Knight's avatar Robert Knight

Implement "uriChanged" event in HTMLIntegration

Add an event that fires when the URI / URL of the current document, as
reported by the `uri` method, changes.
parent c5bfe0c9
......@@ -7,6 +7,7 @@ import {
guessMainContentArea,
preserveScrollPosition,
} from './html-side-by-side';
import { NavigationObserver } from '../util/navigation-observer';
import { scrollElementIntoView } from '../util/scroll';
/**
......@@ -45,6 +46,7 @@ export class HTMLIntegration extends TinyEmitter {
this.describe = describe;
this._htmlMeta = new HTMLMetadata();
this._prevURI = this._htmlMeta.uri();
/** Whether to attempt to resize the document to fit alongside sidebar. */
this._sideBySideEnabled = this.features.flagEnabled('html_side_by_side');
......@@ -58,6 +60,28 @@ export class HTMLIntegration extends TinyEmitter {
/** @type {SidebarLayout|null} */
this._lastLayout = null;
// Watch for changes to `location.href`.
this._navObserver = new NavigationObserver(() => this._checkForURIChange());
// Watch for potential changes to location information in `<head>`, eg.
// `<link rel=canonical>`.
this._metaObserver = new MutationObserver(() => this._checkForURIChange());
this._metaObserver.observe(document.head, {
childList: true,
subtree: true,
attributes: true,
attributeFilter: [
// Keys and values of <link> elements
'rel',
'href',
// Keys and values of <meta> elements
'name',
'content',
],
});
this._flagsChanged = () => {
const sideBySide = features.flagEnabled('html_side_by_side');
if (sideBySide !== this._sideBySideEnabled) {
......@@ -73,11 +97,21 @@ export class HTMLIntegration extends TinyEmitter {
this.features.on('flagsChanged', this._flagsChanged);
}
_checkForURIChange() {
const currentURI = this._htmlMeta.uri();
if (currentURI !== this._prevURI) {
this._prevURI = currentURI;
this.emit('uriChanged', currentURI);
}
}
canAnnotate() {
return true;
}
destroy() {
this._navObserver.disconnect();
this._metaObserver.disconnect();
this.features.off('flagsChanged', this._flagsChanged);
}
......
import { delay } from '../../../test-util/wait';
import { FeatureFlags } from '../../features';
import { HTMLIntegration, $imports } from '../html';
......@@ -8,6 +9,7 @@ describe('HTMLIntegration', () => {
let fakeGuessMainContentArea;
let fakePreserveScrollPosition;
let fakeScrollElementIntoView;
let notifyNavigation;
beforeEach(() => {
features = new FeatureFlags();
......@@ -22,6 +24,13 @@ describe('HTMLIntegration', () => {
uri: sinon.stub().returns('https://example.com/'),
};
class FakeNavigationObserver {
constructor(callback) {
notifyNavigation = callback;
this.disconnect = sinon.stub();
}
}
fakeScrollElementIntoView = sinon.stub().resolves();
fakeGuessMainContentArea = sinon.stub().returns(null);
......@@ -30,6 +39,9 @@ describe('HTMLIntegration', () => {
const HTMLMetadata = sinon.stub().returns(fakeHTMLMetadata);
$imports.$mock({
'../anchoring/html': fakeHTMLAnchoring,
'../util/navigation-observer': {
NavigationObserver: FakeNavigationObserver,
},
'../util/scroll': {
scrollElementIntoView: fakeScrollElementIntoView,
},
......@@ -354,4 +366,66 @@ describe('HTMLIntegration', () => {
assert.deepEqual(await integration.uri(), 'https://example.com/');
});
});
it('emits "uriChanged" event when URL changes after a navigation', () => {
const integration = createIntegration();
const onURIChanged = sinon.stub();
integration.on('uriChanged', onURIChanged);
fakeHTMLMetadata.uri.returns('https://example.com/new-url');
notifyNavigation();
assert.calledWith(onURIChanged, 'https://example.com/new-url');
});
it('emits "uriChanged" event when URL changes after a <head> change', async () => {
const linkEl = document.createElement('link');
linkEl.rel = 'dummy';
linkEl.href = 'https://example.com/dummy';
const integration = createIntegration();
const onURIChanged = sinon.stub();
integration.on('uriChanged', onURIChanged);
try {
document.head.append(linkEl);
fakeHTMLMetadata.uri.returns('https://example.com/new-url');
await delay(0); // Wait for MutationObserver
assert.calledWith(onURIChanged, 'https://example.com/new-url');
} finally {
linkEl.remove();
}
});
it('does not emit "uriChanged" if URL has not changed after a navigation', () => {
const integration = createIntegration();
const onURIChanged = sinon.stub();
integration.on('uriChanged', onURIChanged);
notifyNavigation();
assert.notCalled(onURIChanged);
});
it('does not emit "uriChanged" if URL has not changed after a <head> change', async () => {
const linkEl = document.createElement('link');
linkEl.rel = 'dummy';
linkEl.href = 'https://example.com/dummy';
const integration = createIntegration();
const onURIChanged = sinon.stub();
integration.on('uriChanged', onURIChanged);
try {
document.head.append(linkEl);
await delay(0); // Wait for MutationObserver
assert.notCalled(onURIChanged);
} finally {
linkEl.remove();
}
});
});
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment