Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Only send annotations to matching frame #4131

Merged
merged 4 commits into from
Feb 2, 2022

Conversation

robertknight
Copy link
Member

@robertknight robertknight commented Jan 21, 2022

Depends on #4129

When the sidebar is connected to multiple guest frames it will send all
incoming annotations to all frames. The result is typically that the
annotation will anchor in one frame and orphan in the others. Depending
on what order this happens in, the annotation will non-deterministically
show up as an Annotation or Orphan in the sidebar.

In order to determine which frames an annotation should be sent to in
all cases, we'd either need the backend to return information about which
search URIs an annotation matches or make a separate search request for
each frame and record the associated frame with the results. This
will require some significant refactoring of the annotation search
service.

As an interim step, make FrameSyncService send annotations only to a
single frame based on matching URL, with a fallback to sending to the
main frame if there is no exact match. This will work as expected for
most pages, and is at least deterministic when it does fail. When we
have a solution for being able to match annotations to frames more
generally, we can adapt this code to use it.

This is a partial solution to #3992.

TODO:

  • FrameSyncService needs some additional tests to cover the new behaviors.
  • Test this with VitalSource using the browser extension

Testing:

In one of the dev server test pages that contains an annotation-enabled iframe, try annotating the host frame (if possible) and the iframe, and then reload the page several times.

On master, annotations may sometimes appear in the "Orphans" tab. On this branch, all the annotations should consistently anchor in the expected frame.

@codecov
Copy link

codecov bot commented Jan 21, 2022

Codecov Report

Merging #4131 (989be8c) into master (9465de9) will increase coverage by 0.00%.
The diff coverage is 100.00%.

Impacted file tree graph

@@           Coverage Diff           @@
##           master    #4131   +/-   ##
=======================================
  Coverage   99.08%   99.09%           
=======================================
  Files         219      219           
  Lines        8229     8249   +20     
  Branches     1919     1923    +4     
=======================================
+ Hits         8154     8174   +20     
  Misses         75       75           
Impacted Files Coverage Δ
src/sidebar/services/frame-sync.js 100.00% <100.00%> (ø)

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 9465de9...989be8c. Read the comment docs.

@robertknight
Copy link
Member Author

I'm seeing an issue when testing this branch with VitalSource using the browser extension. The guest frame appears to be receiving an unload just after the guest has loaded and connected to the sidebar. This results in the guest being removed from the sidebar immediately after it is added.

@robertknight
Copy link
Member Author

I'm seeing an issue when testing this branch with VitalSource using the browser extension. The guest frame appears to be receiving an unload just after the guest has loaded and connected to the sidebar

It looks like what is happening here is that chapter content loads in several phases:

  1. The iframe for the previous chapter is removed
  2. An iframe is created for the new chapter and navigated to an initial URL. For example https://jigsaw.vitalsource.com/books/9781319367077/epub/OEBPS/xhtml/lun_9781319056261_ch01_04.xhtml?brand=vitalsource&create=true
  3. The iframe created in step 2 is then navigated to a new URL via a form submission. The form action URL has the same URL as step 2 but with favre=brett query params. For example https://jigsaw.vitalsource.com/books/9781319367077/epub/OEBPS/xhtml/lun_9781319056261_ch01_04.xhtml?brand=vitalsource&create=true&favre=brett.

If the client gets injected into the new frame after step 2, then it gets unloaded in step 3 and doesn't get re-injected, because the VitalSource integration only monitors for creation of iframes, not navigation of existing iframes.

@robertknight robertknight force-pushed the disconnect-guest-when-unloaded branch from e34cffb to 0247587 Compare January 26, 2022 14:47
@robertknight robertknight force-pushed the send-annotation-to-single-frame branch from 6ded1d1 to 752da85 Compare January 26, 2022 14:48
let frameIdentifier = /** @type {string|null} */ (
`temp-${this._nextGuestId}`
);
this._guestRPC.set(frameIdentifier, guestRPC);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this PR I have not changed how frame IDs are allocated. If we change this in future so that frame IDs are either allocated entirely within the sidebar or in such a way that we can know them as soon as the guest connects, then we can simplify this code.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the future, we could easily add a capability to PortFinder#discover to send a frame identifier.

Alternatively, if gathering the information for the documentInfoChanged event is slow, the frame identifier could be send using a new event (decoupled from documentInfoChanged). The frame identifier that are currently sending via documentInfoChanged is immediately available in the guest frame.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the future, we could easily add a capability to PortFinder#discover to send a frame identifier.

Yes. That's definitely one possibility.

@robertknight robertknight marked this pull request as ready for review January 27, 2022 13:45
@robertknight
Copy link
Member Author

Regarding the VitalSource comments above, this was addressed by #4145.

@robertknight robertknight force-pushed the disconnect-guest-when-unloaded branch from 0247587 to 06df56a Compare January 31, 2022 12:16
@esanzgar esanzgar linked an issue Feb 1, 2022 that may be closed by this pull request
@robertknight robertknight force-pushed the send-annotation-to-single-frame branch from 254ee79 to 9765327 Compare February 1, 2022 16:04
Copy link
Contributor

@esanzgar esanzgar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks good. I left a couple of comments.

@@ -36,6 +36,30 @@ export function formatAnnot({ $tag, target, uri }) {
};
}

/**
* Return the frame in `frames` which best matches `ann`.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* Return the frame in `frames` which best matches `ann`.
* Return the frame which best matches the annotation.

Comment on lines 46 to 45
// Choose the frame whose URL exactly matches this annotation. If there is
// none, we'll use the main frame.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think these couple of lines should be included in the functions's documentation.

let frameIdentifier = /** @type {string|null} */ (
`temp-${this._nextGuestId}`
);
this._guestRPC.set(frameIdentifier, guestRPC);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the future, we could easily add a capability to PortFinder#discover to send a frame identifier.

Alternatively, if gathering the information for the documentInfoChanged event is slow, the frame identifier could be send using a new event (decoupled from documentInfoChanged). The frame identifier that are currently sending via documentInfoChanged is immediately available in the guest frame.

Base automatically changed from disconnect-guest-when-unloaded to master February 2, 2022 14:10
When the sidebar is connected to multiple guest frames it will send all
incoming annotations to all frames. The result is typically that the
annotation will anchor in one frame and orphan in the others. Depending
on what order this happens in, the annotation will non-deterministically
show up as an Annotation or Orphan in the sidebar.

In order to determine which frames an annotation should be sent to in
all cases, we'd either need the backend to return information about which
search URIs an annotation matches or make a separate search request for
each frame and record the associated frame with the results. This
will require some significant refactoring of the annotation search
service.

As an interim step, make `FrameSyncService` send annotations only to a
single frame based on matching URL, with a fallback to sending to the
main frame if there is no exact match. This will work as expected for
most pages, and is at least deterministic when it does fail. When we
have a solution for being able to match annotations to frames more
generally, we can adapt this code to use it.

This is a partial solution to #3992.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Inconsistent re-anchoring in multi-guest scenarios
2 participants