follow links for multi-frames in viewport

jh-changes
Shin'ya Ueoka 7 years ago
parent ac5354020e
commit 45b3b0510f
  1. 1
      .eslintrc
  2. 31
      src/content/components/common/follow.js
  3. 18
      src/content/components/top-content/follow-controller.js
  4. 5
      test/content/components/common/follow.test.js

@ -29,6 +29,7 @@
"id-length": "off", "id-length": "off",
"indent": ["error", 2], "indent": ["error", 2],
"jsx-quotes": ["error", "prefer-single"], "jsx-quotes": ["error", "prefer-single"],
"max-params": ["error", 5],
"max-statements": ["error", 15], "max-statements": ["error", 15],
"multiline-ternary": "off", "multiline-ternary": "off",
"newline-after-var": "off", "newline-after-var": "off",

@ -6,16 +6,25 @@ const TARGET_SELECTOR = [
'[contenteditable=true]', '[contenteditable=""]' '[contenteditable=true]', '[contenteditable=""]'
].join(','); ].join(',');
const inWindow = (win, element) => { const inViewport = (win, element, viewSize, framePosition) => {
let { let {
top, left, bottom, right top, left, bottom, right
} = element.getBoundingClientRect(); } = element.getBoundingClientRect();
let doc = win.doc; let doc = win.doc;
return ( let frameWidth = win.innerWidth || doc.documentElement.clientWidth;
top >= 0 && left >= 0 && let frameHeight = win.innerHeight || doc.documentElement.clientHeight;
bottom <= (win.innerHeight || doc.documentElement.clientHeight) &&
right <= (win.innerWidth || doc.documentElement.clientWidth) if (right < 0 || bottom < 0 || top > frameHeight || left > frameWidth) {
); // out of frame
return false;
}
if (right + framePosition.x < 0 || bottom + framePosition.y < 0 ||
left + framePosition.x > viewSize.width ||
top + framePosition.y > viewSize.height) {
// out of viewport
return false;
}
return true;
}; };
export default class Follow { export default class Follow {
@ -60,8 +69,8 @@ export default class Follow {
}); });
} }
countHints(sender) { countHints(sender, viewSize, framePosition) {
this.targets = Follow.getTargetElements(this.win); this.targets = Follow.getTargetElements(this.win, viewSize, framePosition);
sender.postMessage(JSON.stringify({ sender.postMessage(JSON.stringify({
type: messages.FOLLOW_RESPONSE_COUNT_TARGETS, type: messages.FOLLOW_RESPONSE_COUNT_TARGETS,
count: this.targets.length, count: this.targets.length,
@ -133,7 +142,7 @@ export default class Follow {
onMessage(message, sender) { onMessage(message, sender) {
switch (message.type) { switch (message.type) {
case messages.FOLLOW_REQUEST_COUNT_TARGETS: case messages.FOLLOW_REQUEST_COUNT_TARGETS:
return this.countHints(sender); return this.countHints(sender, message.viewSize, message.framePosition);
case messages.FOLLOW_CREATE_HINTS: case messages.FOLLOW_CREATE_HINTS:
return this.createHints(message.keysArray, message.newTab); return this.createHints(message.keysArray, message.newTab);
case messages.FOLLOW_SHOW_HINTS: case messages.FOLLOW_SHOW_HINTS:
@ -145,7 +154,7 @@ export default class Follow {
} }
} }
static getTargetElements(win) { static getTargetElements(win, viewSize, framePosition) {
let all = win.document.querySelectorAll(TARGET_SELECTOR); let all = win.document.querySelectorAll(TARGET_SELECTOR);
let filtered = Array.prototype.filter.call(all, (element) => { let filtered = Array.prototype.filter.call(all, (element) => {
let style = win.getComputedStyle(element); let style = win.getComputedStyle(element);
@ -153,7 +162,7 @@ export default class Follow {
style.visibility !== 'hidden' && style.visibility !== 'hidden' &&
element.type !== 'hidden' && element.type !== 'hidden' &&
element.offsetHeight > 0 && element.offsetHeight > 0 &&
inWindow(win, element); inViewport(win, element, viewSize, framePosition);
}); });
return filtered; return filtered;
} }

@ -87,8 +87,24 @@ export default class FollowController {
count() { count() {
this.producer = new HintKeyProducer(DEFAULT_HINT_CHARSET); this.producer = new HintKeyProducer(DEFAULT_HINT_CHARSET);
broadcastMessage(this.win, { let doc = this.win.document;
let viewWidth = this.win.innerWidth || doc.documentElement.clientWidth;
let viewHeight = this.win.innerHeight || doc.documentElement.clientHeight;
let frameElements = this.win.document.querySelectorAll('frame,iframe');
this.win.postMessage(JSON.stringify({
type: messages.FOLLOW_REQUEST_COUNT_TARGETS, type: messages.FOLLOW_REQUEST_COUNT_TARGETS,
viewSize: { width: viewWidth, height: viewHeight },
framePosition: { x: 0, y: 0 },
}), '*');
frameElements.forEach((element) => {
let { left: frameX, top: frameY } = element.getBoundingClientRect();
let message = JSON.stringify({
type: messages.FOLLOW_REQUEST_COUNT_TARGETS,
viewSize: { width: viewWidth, height: viewHeight },
framePosition: { x: frameX, y: frameY },
});
element.contentWindow.postMessage(message, '*');
}); });
} }

@ -8,7 +8,10 @@ describe('FollowComponent', () => {
}); });
it('returns visible links', () => { it('returns visible links', () => {
let targets = FollowComponent.getTargetElements(window); let targets = FollowComponent.getTargetElements(
window,
{ width: window.innerWidth, height: window.innerHeight },
{ x: 0, y: 0 });
expect(targets).to.have.lengthOf(3); expect(targets).to.have.lengthOf(3);
let ids = Array.prototype.map.call(targets, (e) => e.id); let ids = Array.prototype.map.call(targets, (e) => e.id);