Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
C
coopwire-hypothesis
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
孙灵跃 Leon Sun
coopwire-hypothesis
Commits
054f8184
Commit
054f8184
authored
Jun 27, 2017
by
Sean Roberts
Committed by
GitHub
Jun 27, 2017
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #457 from evidentpoint/multi-frame-fixes
Multiple frame detection improvements
parents
6a107abd
44395004
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
168 additions
and
38 deletions
+168
-38
frame-observer.js
src/annotator/frame-observer.js
+76
-0
cross-frame.coffee
src/annotator/plugin/cross-frame.coffee
+6
-36
multi-frame-test.js
src/annotator/test/integration/multi-frame-test.js
+75
-2
frame-util.js
src/annotator/util/frame-util.js
+11
-0
No files found.
src/annotator/frame-observer.js
0 → 100644
View file @
054f8184
'use strict'
;
let
FrameUtil
=
require
(
'./util/frame-util'
);
let
debounce
=
require
(
'lodash.debounce'
);
// Find difference of two arrays
let
difference
=
(
arrayA
,
arrayB
)
=>
{
return
arrayA
.
filter
(
x
=>
!
arrayB
.
includes
(
x
));
};
const
DEBOUNCE_WAIT
=
40
;
class
FrameObserver
{
constructor
(
target
)
{
this
.
_target
=
target
;
this
.
_handledFrames
=
[];
this
.
_mutationObserver
=
new
MutationObserver
(
debounce
(()
=>
{
this
.
_discoverFrames
();
},
DEBOUNCE_WAIT
));
}
observe
(
onFrameAddedCallback
,
onFrameRemovedCallback
)
{
this
.
_onFrameAdded
=
onFrameAddedCallback
;
this
.
_onFrameRemoved
=
onFrameRemovedCallback
;
this
.
_discoverFrames
();
this
.
_mutationObserver
.
observe
(
this
.
_target
,
{
childList
:
true
,
subtree
:
true
,
});
}
disconnect
()
{
this
.
_mutationObserver
.
disconnect
();
}
_addFrame
(
frame
)
{
if
(
FrameUtil
.
isAccessible
(
frame
))
{
FrameUtil
.
isDocumentReady
(
frame
,
()
=>
{
frame
.
contentWindow
.
addEventListener
(
'unload'
,
()
=>
{
this
.
_removeFrame
(
frame
);
});
this
.
_handledFrames
.
push
(
frame
);
this
.
_onFrameAdded
(
frame
);
});
}
else
{
// Could warn here that frame was not cross origin accessible
}
}
_removeFrame
(
frame
)
{
this
.
_onFrameRemoved
(
frame
);
// Remove the frame from our list
this
.
_handledFrames
=
this
.
_handledFrames
.
filter
(
x
=>
x
!==
frame
);
}
_discoverFrames
()
{
let
frames
=
FrameUtil
.
findFrames
(
this
.
_target
);
for
(
let
frame
of
frames
)
{
if
(
!
this
.
_handledFrames
.
includes
(
frame
))
{
this
.
_addFrame
(
frame
);
}
}
for
(
let
frame
of
difference
(
this
.
_handledFrames
,
frames
))
{
this
.
_removeFrame
(
frame
);
}
}
}
FrameObserver
.
DEBOUNCE_WAIT
=
DEBOUNCE_WAIT
;
module
.
exports
=
FrameObserver
;
\ No newline at end of file
src/annotator/plugin/cross-frame.coffee
View file @
054f8184
...
@@ -4,8 +4,7 @@ AnnotationSync = require('../annotation-sync')
...
@@ -4,8 +4,7 @@ AnnotationSync = require('../annotation-sync')
Bridge
=
require
(
'../../shared/bridge'
)
Bridge
=
require
(
'../../shared/bridge'
)
Discovery
=
require
(
'../../shared/discovery'
)
Discovery
=
require
(
'../../shared/discovery'
)
FrameUtil
=
require
(
'../util/frame-util'
)
FrameUtil
=
require
(
'../util/frame-util'
)
FrameObserver
=
require
(
'../frame-observer'
)
debounce
=
require
(
'lodash.debounce'
)
# Extracts individual keys from an object and returns a new one.
# Extracts individual keys from an object and returns a new one.
extract
=
extract
=
(
obj
,
keys
...)
->
extract
=
extract
=
(
obj
,
keys
...)
->
...
@@ -13,11 +12,6 @@ extract = extract = (obj, keys...) ->
...
@@ -13,11 +12,6 @@ extract = extract = (obj, keys...) ->
ret
[
key
]
=
obj
[
key
]
for
key
in
keys
when
obj
.
hasOwnProperty
(
key
)
ret
[
key
]
=
obj
[
key
]
for
key
in
keys
when
obj
.
hasOwnProperty
(
key
)
ret
ret
# Find difference of two arrays
difference
=
(
arrayA
,
arrayB
)
->
arrayA
.
filter
(
x
)
->
!
arrayB
.
includes
(
x
)
# Class for establishing a messaging connection to the parent sidebar as well
# Class for establishing a messaging connection to the parent sidebar as well
# as keeping the annotation state in sync with the sidebar application, this
# as keeping the annotation state in sync with the sidebar application, this
# frame acts as the bridge client, the sidebar is the server. This plugin
# frame acts as the bridge client, the sidebar is the server. This plugin
...
@@ -35,8 +29,7 @@ module.exports = class CrossFrame extends Plugin
...
@@ -35,8 +29,7 @@ module.exports = class CrossFrame extends Plugin
opts
=
extract
(
options
,
'on'
,
'emit'
)
opts
=
extract
(
options
,
'on'
,
'emit'
)
annotationSync
=
new
AnnotationSync
(
bridge
,
opts
)
annotationSync
=
new
AnnotationSync
(
bridge
,
opts
)
frameObserver
=
new
FrameObserver
(
elem
)
handledFrames
=
[]
this
.
pluginInit
=
->
this
.
pluginInit
=
->
onDiscoveryCallback
=
(
source
,
origin
,
token
)
->
onDiscoveryCallback
=
(
source
,
origin
,
token
)
->
...
@@ -44,13 +37,14 @@ module.exports = class CrossFrame extends Plugin
...
@@ -44,13 +37,14 @@ module.exports = class CrossFrame extends Plugin
discovery
.
startDiscovery
(
onDiscoveryCallback
)
discovery
.
startDiscovery
(
onDiscoveryCallback
)
if
options
.
enableMultiFrameSupport
if
options
.
enableMultiFrameSupport
_setupFrameDetection
()
frameObserver
.
observe
(
_injectToFrame
,
_iframeUnloaded
);
this
.
destroy
=
->
this
.
destroy
=
->
# super doesnt work here :(
# super doesnt work here :(
Plugin
::
destroy
.
apply
(
this
,
arguments
)
Plugin
::
destroy
.
apply
(
this
,
arguments
)
bridge
.
destroy
()
bridge
.
destroy
()
discovery
.
stopDiscovery
()
discovery
.
stopDiscovery
()
frameObserver
.
disconnect
()
this
.
sync
=
(
annotations
,
cb
)
->
this
.
sync
=
(
annotations
,
cb
)
->
annotationSync
.
sync
(
annotations
,
cb
)
annotationSync
.
sync
(
annotations
,
cb
)
...
@@ -64,34 +58,10 @@ module.exports = class CrossFrame extends Plugin
...
@@ -64,34 +58,10 @@ module.exports = class CrossFrame extends Plugin
this
.
onConnect
=
(
fn
)
->
this
.
onConnect
=
(
fn
)
->
bridge
.
onConnect
(
fn
)
bridge
.
onConnect
(
fn
)
_setupFrameDetection
=
->
_discoverOwnFrames
()
# Listen for DOM mutations, to know when frames are added / removed
observer
=
new
MutationObserver
(
debounce
(
_discoverOwnFrames
,
300
,
leading
:
true
))
observer
.
observe
(
elem
,
{
childList
:
true
,
subtree
:
true
});
_discoverOwnFrames
=
->
frames
=
FrameUtil
.
findFrames
(
elem
)
for
frame
in
frames
if
frame
not
in
handledFrames
_handleFrame
(
frame
)
handledFrames
.
push
(
frame
)
for
frame
,
i
in
difference
(
handledFrames
,
frames
)
_iframeUnloaded
(
frame
)
delete
handledFrames
[
i
]
_injectToFrame
=
(
frame
)
->
_injectToFrame
=
(
frame
)
->
if
!
FrameUtil
.
hasHypothesis
(
frame
)
if
!
FrameUtil
.
hasHypothesis
(
frame
)
FrameUtil
.
injectHypothesis
(
frame
,
options
.
embedScriptUrl
)
FrameUtil
.
isLoaded
frame
,
()
->
frame
.
contentWindow
.
addEventListener
'unload'
,
->
FrameUtil
.
injectHypothesis
(
frame
,
options
.
embedScriptUrl
)
_iframeUnloaded
(
frame
)
_handleFrame
=
(
frame
)
->
if
!
FrameUtil
.
isAccessible
(
frame
)
then
return
FrameUtil
.
isLoaded
frame
,
()
->
_injectToFrame
(
frame
)
_iframeUnloaded
=
(
frame
)
->
_iframeUnloaded
=
(
frame
)
->
# TODO: Bridge call here not yet implemented, placeholder for now
# TODO: Bridge call here not yet implemented, placeholder for now
...
...
src/annotator/test/integration/multi-frame-test.js
View file @
054f8184
'use strict'
;
'use strict'
;
var
proxyquire
=
require
(
'proxyquire'
);
var
proxyquire
=
require
(
'proxyquire'
);
var
isLoaded
=
require
(
'../../util/frame-util.js'
).
isLoaded
;
var
isLoaded
=
require
(
'../../util/frame-util'
).
isLoaded
;
var
FRAME_ADD_WAIT
=
require
(
'../../frame-observer'
).
DEBOUNCE_WAIT
+
10
;
describe
(
'CrossFrame multi-frame scenario'
,
function
()
{
describe
(
'CrossFrame multi-frame scenario'
,
function
()
{
var
fakeAnnotationSync
;
var
fakeAnnotationSync
;
...
@@ -143,7 +145,7 @@ describe('CrossFrame multi-frame scenario', function () {
...
@@ -143,7 +145,7 @@ describe('CrossFrame multi-frame scenario', function () {
'expected dynamically added frame to be modified'
);
'expected dynamically added frame to be modified'
);
resolve
();
resolve
();
});
});
},
0
);
},
FRAME_ADD_WAIT
);
});
});
});
});
...
@@ -169,4 +171,75 @@ describe('CrossFrame multi-frame scenario', function () {
...
@@ -169,4 +171,75 @@ describe('CrossFrame multi-frame scenario', function () {
});
});
});
});
it
(
'detects a frame dynamically removed, and added again'
,
function
()
{
// Create a frame before initializing
var
frame
=
document
.
createElement
(
'iframe'
);
container
.
appendChild
(
frame
);
// Now initialize
crossFrame
.
pluginInit
();
return
new
Promise
(
function
(
resolve
)
{
isLoaded
(
frame
,
function
()
{
assert
(
frame
.
contentDocument
.
body
.
hasChildNodes
(),
'expected initial frame to be modified'
);
frame
.
remove
();
// Yield to let the DOM and CrossFrame catch up
setTimeout
(
function
()
{
// Add the frame again
container
.
appendChild
(
frame
);
// Yield again
setTimeout
(
function
()
{
isLoaded
(
frame
,
function
()
{
assert
(
frame
.
contentDocument
.
body
.
hasChildNodes
(),
'expected dynamically added frame to be modified'
);
resolve
();
});
},
FRAME_ADD_WAIT
);
},
0
);
});
});
});
it
(
'detects a frame dynamically added, removed, and added again'
,
function
()
{
// Initialize with no initial frame
crossFrame
.
pluginInit
();
// Add a frame to the DOM
var
frame
=
document
.
createElement
(
'iframe'
);
container
.
appendChild
(
frame
);
return
new
Promise
(
function
(
resolve
)
{
// Yield to let the DOM and CrossFrame catch up
setTimeout
(
function
()
{
isLoaded
(
frame
,
function
()
{
assert
(
frame
.
contentDocument
.
body
.
hasChildNodes
(),
'expected dynamically added frame to be modified'
);
frame
.
remove
();
// Yield again
setTimeout
(
function
()
{
// Add the frame again
container
.
appendChild
(
frame
);
// Yield
setTimeout
(
function
()
{
isLoaded
(
frame
,
function
()
{
assert
(
frame
.
contentDocument
.
body
.
hasChildNodes
(),
'expected dynamically added frame to be modified'
);
resolve
();
});
},
FRAME_ADD_WAIT
);
},
0
);
});
},
FRAME_ADD_WAIT
);
});
});
});
});
\ No newline at end of file
src/annotator/util/frame-util.js
View file @
054f8184
...
@@ -43,6 +43,16 @@ function isValid (iframe) {
...
@@ -43,6 +43,16 @@ function isValid (iframe) {
return
iframe
.
className
!==
'h-sidebar-iframe'
;
return
iframe
.
className
!==
'h-sidebar-iframe'
;
}
}
function
isDocumentReady
(
iframe
,
callback
)
{
if
(
iframe
.
contentDocument
.
readyState
===
'loading'
)
{
iframe
.
contentDocument
.
addEventListener
(
'DOMContentLoaded'
,
function
()
{
callback
();
});
}
else
{
callback
();
}
}
function
isLoaded
(
iframe
,
callback
)
{
function
isLoaded
(
iframe
,
callback
)
{
if
(
iframe
.
contentDocument
.
readyState
!==
'complete'
)
{
if
(
iframe
.
contentDocument
.
readyState
!==
'complete'
)
{
iframe
.
addEventListener
(
'load'
,
function
()
{
iframe
.
addEventListener
(
'load'
,
function
()
{
...
@@ -60,4 +70,5 @@ module.exports = {
...
@@ -60,4 +70,5 @@ module.exports = {
isAccessible
:
isAccessible
,
isAccessible
:
isAccessible
,
isValid
:
isValid
,
isValid
:
isValid
,
isLoaded
:
isLoaded
,
isLoaded
:
isLoaded
,
isDocumentReady
:
isDocumentReady
,
};
};
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment