This is a minimal functioning example of the use of RSelenium. Selenium opens a browser and points, clicks, types, etc. to interact with a dynamic website as you might do manually.

We will use it to search the UN documents database for documents using the term “migration” (in UN Session 68) – a classmate requested example – navigate to the English pdfs on the first page of results, download three of them, and then go to the next page.

A comprehensive collection would need to iterate over sessions or dates systematically (in sufficiently small chunks to avoid capping out at the maximum results of 500), download all 20 documents on a given page (perhaps also or instead retrieving the Word document version; perhaps also or instead retrieving documents in other languages), click through to all pages of the search result, collecting all the documents on each.

  1. This will take a long time for a broad search term like we use here. The robots.txt file seems to allow crawling here, with a 10 second delay between requests.

  2. This is making database queries, so be gentle. The 10-second delay is more than reasonable. Aggressive crawling here may effectively turn into a denial-of-service attack, and could be reasonably interpreted as such.

  3. Selenium takes over your computer while it’s operating. Long intensive scrapes like this can have memory leaks and similar effects that can crash your computer.

library(RSelenium)

This is a bit esoteric, but you’re basically opening a browser instance and sending it to the UN search page. In this case, we are using Firefox. You can also use Chrome, but I’ve had more luck with Firefox. The first command below sets up Firefox when it opens so that it downloads pdf files rather than opening them in the browser window.

# Define a profile for Firefox that downloads pdf files instead of opening them in the browser with a plugin
pdfprof <- makeFirefoxProfile(list(
  "pdfjs.disabled"=TRUE,
  "plugin.scan.plid.all"=FALSE,
  "plugin.scan.Acrobat" = "99.0",
  "browser.helperApps.neverAsk.saveToDisk"='application/pdf'))
# Open a firefox browser
mydriver <- rsDriver(browser=c("firefox"),port=4444L, extraCapabilities=pdfprof)
checking Selenium Server versions:
BEGIN: PREDOWNLOAD
BEGIN: DOWNLOAD
BEGIN: POSTDOWNLOAD
checking chromedriver versions:
BEGIN: PREDOWNLOAD
BEGIN: DOWNLOAD
BEGIN: POSTDOWNLOAD
checking geckodriver versions:
BEGIN: PREDOWNLOAD
BEGIN: DOWNLOAD
BEGIN: POSTDOWNLOAD
checking phantomjs versions:
BEGIN: PREDOWNLOAD
BEGIN: DOWNLOAD
BEGIN: POSTDOWNLOAD
[1] "Connecting to remote server"
$acceptInsecureCerts
[1] FALSE

$browserName
[1] "firefox"

$browserVersion
[1] "65.0.1"

$`moz:accessibilityChecks`
[1] FALSE

$`moz:geckodriverVersion`
[1] "0.26.0"

$`moz:headless`
[1] FALSE

$`moz:processID`
[1] 47164

$`moz:profile`
[1] "/var/folders/c5/29_g0scs1qz93cp83_bpwn540000gn/T/rust_mozprofileumM7qG"

$`moz:shutdownTimeout`
[1] 60000

$`moz:useNonSpecCompliantPointerOrigin`
[1] FALSE

$`moz:webdriverClick`
[1] TRUE

$pageLoadStrategy
[1] "normal"

$platformName
[1] "mac"

$platformVersion
[1] "16.7.0"

$rotatable
[1] FALSE

$setWindowRect
[1] TRUE

$strictFileInteractability
[1] FALSE

$timeouts
$timeouts$implicit
[1] 0

$timeouts$pageLoad
[1] 300000

$timeouts$script
[1] 30000


$unhandledPromptBehavior
[1] "dismiss and notify"

$webdriver.remote.sessionid
[1] "0e8deb1d-8769-a546-a119-ae3829bf203c"

$id
[1] "0e8deb1d-8769-a546-a119-ae3829bf203c"
remDr <- mydriver[['client']]
remDr$open()
[1] "Connecting to remote server"
$acceptInsecureCerts
[1] FALSE

$browserName
[1] "firefox"

$browserVersion
[1] "65.0.1"

$`moz:accessibilityChecks`
[1] FALSE

$`moz:geckodriverVersion`
[1] "0.26.0"

$`moz:headless`
[1] FALSE

$`moz:processID`
[1] 47178

$`moz:profile`
[1] "/var/folders/c5/29_g0scs1qz93cp83_bpwn540000gn/T/rust_mozprofileUuO31D"

$`moz:shutdownTimeout`
[1] 60000

$`moz:useNonSpecCompliantPointerOrigin`
[1] FALSE

$`moz:webdriverClick`
[1] TRUE

$pageLoadStrategy
[1] "normal"

$platformName
[1] "mac"

$platformVersion
[1] "16.7.0"

$rotatable
[1] FALSE

$setWindowRect
[1] TRUE

$strictFileInteractability
[1] FALSE

$timeouts
$timeouts$implicit
[1] 0

$timeouts$pageLoad
[1] 300000

$timeouts$script
[1] 30000


$unhandledPromptBehavior
[1] "dismiss and notify"

$webdriver.remote.sessionid
[1] "942d9e79-cc64-c140-9765-302eace5ccb5"

$id
[1] "942d9e79-cc64-c140-9765-302eace5ccb5"
# Open the UN search page
remDr$navigate("https://documents.un.org/")

You should see a firefox window open and look like this:



Use SelectorGadget (https://selectorgadget.com/) to identify css selectors associated with the page elements you are interested in. (See the “Scraping with rvest” Tutorial for details where to get and how to use SelectorGadget.)

We’re going to need to interact with three elements of the page. We first need to have Selenium find the text box for “Session” and type in “68”. Then we need it to find the text box for full text search and type in “migration”. Finally, we need to to find the search button and click it.

Note that the selector strings for some of these include escape characters. As we did with regular expressions, you will need to double these up for R to interpret them correctly.

# Find the text box for Session
sessionbox_Element <-
  remDr$findElement(using="css",                     value="#view\\:_id1\\:_id2\\:txtSess")
# Type the session number into it
sessionbox_Element$sendKeysToElement(list("68"))
# Find the text box for full text search
fulltextbox_Element <-
  remDr$findElement(using="css",                      value="#view\\:_id1\\:_id2\\:txtFTSrch")
# Type the search term in
fulltextbox_Element$sendKeysToElement(list("migration"))

Now you’re browser window should look like this:



Now click search.

# Find the search button
searchbutton_Element <-
  remDr$findElement(using="css", value="#view\\:_id1\\:_id2\\:btnRefine")
# Click it
searchbutton_Element$clickElement()

That gives us search results at the bottom:


# Find the 20 "Details" arrows
Details_Elements <- remDr$findElements(using="css", value=".odsIcon:nth-child(1)")
# click all of them
for (i in 1:20) { Details_Elements[[i]]$clickElement() }
#sapply(Details_Elements, function(x) x %>% clickElement)
# Find the 20 English pdf links
PDF_Elements <- remDr$findElements(using="css", value=".col-xs-4:nth-child(3) .fa-file-pdf-o")
for (i in 1:3) { #Just get three
  PDF_Elements[[i]]$clickElement()
  Sys.sleep(10)
}

The three files should be in your default Downloads folder. (This can probably be changed with another command in the Firefox profile.)

We’re done with those 20, so go to the next page.

nextpage_Element <-
  remDr$findElement(using="css", value=".disabled+ li a")
nextpage_Element$clickElement()

Repeat the file download on each page and click to the next until you’re done.

Close your browser/server.

mydriver$server$stop()
[1] TRUE

To do this systematically, you would have to put all of that in an automated loop — looping over sessions wanted for new searches, within that looping over pages of the search results, and within that looping over document links to download – which I “leave as an exercise.”

This is VERY time-consuming and asks a lot of the server. I would very closely consider whether I really needed all the documents this broad search returns before I proceeded.

LS0tCnRpdGxlOiAiU2NyYXBpbmcgd2l0aCBSU2VsZW5pdW0iCnN1YnRpdGxlOiAiUExTQyA1OTcsIFRleHQgYXMgRGF0YSwgUGVubiBTdGF0ZSIKYXV0aG9yOiAiQnVydCBMLiBNb25yb2UiCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgY29kZV9mb2xkaW5nOiBzaG93CiAgICBoaWdobGlnaHQ6IHRhbmdvCiAgICB0aGVtZTogdW5pdGVkCiAgICBkZl9wcmludDogcGFnZWQKICAgIHRvYzogeWVzCi0tLQoKVGhpcyBpcyBhIG1pbmltYWwgZnVuY3Rpb25pbmcgZXhhbXBsZSBvZiB0aGUgdXNlIG9mIFJTZWxlbml1bS4gU2VsZW5pdW0gb3BlbnMgYSBicm93c2VyIGFuZCBwb2ludHMsIGNsaWNrcywgdHlwZXMsIGV0Yy4gdG8gaW50ZXJhY3Qgd2l0aCBhIGR5bmFtaWMgd2Vic2l0ZSBhcyB5b3UgbWlnaHQgZG8gbWFudWFsbHkuCgpXZSB3aWxsIHVzZSBpdCB0byBzZWFyY2ggdGhlIFVOIGRvY3VtZW50cyBkYXRhYmFzZSBmb3IgZG9jdW1lbnRzIHVzaW5nIHRoZSB0ZXJtICJtaWdyYXRpb24iIChpbiBVTiBTZXNzaW9uIDY4KSAtLSBhIGNsYXNzbWF0ZSByZXF1ZXN0ZWQgZXhhbXBsZSAtLSBuYXZpZ2F0ZSB0byB0aGUgRW5nbGlzaCBwZGZzIG9uIHRoZSBmaXJzdCBwYWdlIG9mIHJlc3VsdHMsIGRvd25sb2FkIHRocmVlIG9mIHRoZW0sIGFuZCB0aGVuIGdvIHRvIHRoZSBuZXh0IHBhZ2UuCgpBIGNvbXByZWhlbnNpdmUgY29sbGVjdGlvbiB3b3VsZCBuZWVkIHRvIGl0ZXJhdGUgb3ZlciBzZXNzaW9ucyBvciBkYXRlcyBzeXN0ZW1hdGljYWxseSAoaW4gc3VmZmljaWVudGx5IHNtYWxsIGNodW5rcyB0byBhdm9pZCBjYXBwaW5nIG91dCBhdCB0aGUgbWF4aW11bSByZXN1bHRzIG9mIDUwMCksIGRvd25sb2FkIGFsbCAyMCBkb2N1bWVudHMgb24gYSBnaXZlbiBwYWdlIChwZXJoYXBzIGFsc28gb3IgaW5zdGVhZCByZXRyaWV2aW5nIHRoZSBXb3JkIGRvY3VtZW50IHZlcnNpb247IHBlcmhhcHMgYWxzbyBvciBpbnN0ZWFkIHJldHJpZXZpbmcgZG9jdW1lbnRzIGluIG90aGVyIGxhbmd1YWdlcyksIGNsaWNrIHRocm91Z2ggdG8gYWxsIHBhZ2VzIG9mIHRoZSBzZWFyY2ggcmVzdWx0LCBjb2xsZWN0aW5nIGFsbCB0aGUgZG9jdW1lbnRzIG9uIGVhY2guCgooYSkgVGhpcyB3aWxsIHRha2UgYSBsb25nIHRpbWUgZm9yIGEgYnJvYWQgc2VhcmNoIHRlcm0gbGlrZSB3ZSB1c2UgaGVyZS4gVGhlIHJvYm90cy50eHQgZmlsZSBzZWVtcyB0byBhbGxvdyBjcmF3bGluZyBoZXJlLCB3aXRoIGEgMTAgc2Vjb25kIGRlbGF5IGJldHdlZW4gcmVxdWVzdHMuCgooYikgVGhpcyBpcyBtYWtpbmcgZGF0YWJhc2UgcXVlcmllcywgc28gYmUgZ2VudGxlLiBUaGUgMTAtc2Vjb25kIGRlbGF5IGlzIG1vcmUgdGhhbiByZWFzb25hYmxlLiBBZ2dyZXNzaXZlIGNyYXdsaW5nIGhlcmUgbWF5IGVmZmVjdGl2ZWx5IHR1cm4gaW50byBhIGRlbmlhbC1vZi1zZXJ2aWNlIGF0dGFjaywgYW5kIGNvdWxkIGJlIHJlYXNvbmFibHkgaW50ZXJwcmV0ZWQgYXMgc3VjaC4KCihjKSBTZWxlbml1bSB0YWtlcyBvdmVyIHlvdXIgY29tcHV0ZXIgd2hpbGUgaXQncyBvcGVyYXRpbmcuIExvbmcgaW50ZW5zaXZlIHNjcmFwZXMgbGlrZSB0aGlzIGNhbiBoYXZlIG1lbW9yeSBsZWFrcyBhbmQgc2ltaWxhciBlZmZlY3RzIHRoYXQgY2FuIGNyYXNoIHlvdXIgY29tcHV0ZXIuCgpgYGB7cn0KbGlicmFyeShSU2VsZW5pdW0pCmBgYAoKVGhpcyBpcyBhIGJpdCBlc290ZXJpYywgYnV0IHlvdSdyZSBiYXNpY2FsbHkgb3BlbmluZyBhIGJyb3dzZXIgaW5zdGFuY2UgYW5kIHNlbmRpbmcgaXQgdG8gdGhlIFVOIHNlYXJjaCBwYWdlLiBJbiB0aGlzIGNhc2UsIHdlIGFyZSB1c2luZyBGaXJlZm94LiBZb3UgY2FuIGFsc28gdXNlIENocm9tZSwgYnV0IEkndmUgaGFkIG1vcmUgbHVjayB3aXRoIEZpcmVmb3guIFRoZSBmaXJzdCBjb21tYW5kIGJlbG93IHNldHMgdXAgRmlyZWZveCB3aGVuIGl0IG9wZW5zIHNvIHRoYXQgaXQgZG93bmxvYWRzIHBkZiBmaWxlcyByYXRoZXIgdGhhbiBvcGVuaW5nIHRoZW0gaW4gdGhlIGJyb3dzZXIgd2luZG93LgoKYGBge3J9CiMgRGVmaW5lIGEgcHJvZmlsZSBmb3IgRmlyZWZveCB0aGF0IGRvd25sb2FkcyBwZGYgZmlsZXMgaW5zdGVhZCBvZiBvcGVuaW5nIHRoZW0gaW4gdGhlIGJyb3dzZXIgd2l0aCBhIHBsdWdpbgpwZGZwcm9mIDwtIG1ha2VGaXJlZm94UHJvZmlsZShsaXN0KAogICJwZGZqcy5kaXNhYmxlZCI9VFJVRSwKICAicGx1Z2luLnNjYW4ucGxpZC5hbGwiPUZBTFNFLAogICJwbHVnaW4uc2Nhbi5BY3JvYmF0IiA9ICI5OS4wIiwKICAiYnJvd3Nlci5oZWxwZXJBcHBzLm5ldmVyQXNrLnNhdmVUb0Rpc2siPSdhcHBsaWNhdGlvbi9wZGYnKSkKCiMgT3BlbiBhIGZpcmVmb3ggYnJvd3NlcgpteWRyaXZlciA8LSByc0RyaXZlcihicm93c2VyPWMoImZpcmVmb3giKSxwb3J0PTQ0NDRMLCBleHRyYUNhcGFiaWxpdGllcz1wZGZwcm9mKQpyZW1EciA8LSBteWRyaXZlcltbJ2NsaWVudCddXQpyZW1EciRvcGVuKCkKCiMgT3BlbiB0aGUgVU4gc2VhcmNoIHBhZ2UKcmVtRHIkbmF2aWdhdGUoImh0dHBzOi8vZG9jdW1lbnRzLnVuLm9yZy8iKQpgYGAKCllvdSBzaG91bGQgc2VlIGEgZmlyZWZveCB3aW5kb3cgb3BlbiBhbmQgbG9vayBsaWtlIHRoaXM6CgotLS0tLS0KCjxjZW50ZXI+CmBgYHtyLCwgb3V0LndpZHRoPSI3MCUiLCAgb3V0LmV4dHJhPSdzdHlsZT0iYmFja2dyb3VuZC1jb2xvcjogIzllY2ZmNzsgcGFkZGluZzoxMHB4OyAgZGlzcGxheTppbmxpbmUtYmxvY2siJywgZXZhbD1UUlVFLCBlY2hvPUZBTFNFfQoKaW5jbHVkZV9ncmFwaGljcygiaW1hZ2VzLVVOc2VhcmNoL29wZW5yYnJvd3Nlci5qcGVnIikKYGBgCjwvY2VudGVyPgoKLS0tLS0tCgpVc2UgU2VsZWN0b3JHYWRnZXQgKGh0dHBzOi8vc2VsZWN0b3JnYWRnZXQuY29tLykgdG8gaWRlbnRpZnkgY3NzIHNlbGVjdG9ycyBhc3NvY2lhdGVkIHdpdGggdGhlIHBhZ2UgZWxlbWVudHMgeW91IGFyZSBpbnRlcmVzdGVkIGluLiAoU2VlIHRoZSAiU2NyYXBpbmcgd2l0aCBydmVzdCIgVHV0b3JpYWwgZm9yIGRldGFpbHMgd2hlcmUgdG8gZ2V0IGFuZCBob3cgdG8gdXNlIFNlbGVjdG9yR2FkZ2V0LikKCldlJ3JlIGdvaW5nIHRvIG5lZWQgdG8gaW50ZXJhY3Qgd2l0aCB0aHJlZSBlbGVtZW50cyBvZiB0aGUgcGFnZS4gV2UgZmlyc3QgbmVlZCB0byBoYXZlIFNlbGVuaXVtIGZpbmQgdGhlIHRleHQgYm94IGZvciAiU2Vzc2lvbiIgYW5kIHR5cGUgaW4gIjY4Ii4gVGhlbiB3ZSBuZWVkIGl0IHRvIGZpbmQgdGhlIHRleHQgYm94IGZvciBmdWxsIHRleHQgc2VhcmNoIGFuZCB0eXBlIGluICJtaWdyYXRpb24iLiBGaW5hbGx5LCB3ZSBuZWVkIHRvIHRvIGZpbmQgdGhlIHNlYXJjaCBidXR0b24gYW5kIGNsaWNrIGl0LgoKTm90ZSB0aGF0IHRoZSBzZWxlY3RvciBzdHJpbmdzIGZvciBzb21lIG9mIHRoZXNlIGluY2x1ZGUgZXNjYXBlIGNoYXJhY3RlcnMuIEFzIHdlIGRpZCB3aXRoIHJlZ3VsYXIgZXhwcmVzc2lvbnMsIHlvdSB3aWxsIG5lZWQgdG8gZG91YmxlIHRoZXNlIHVwIGZvciBSIHRvIGludGVycHJldCB0aGVtIGNvcnJlY3RseS4KCmBgYHtyfQojIEZpbmQgdGhlIHRleHQgYm94IGZvciBTZXNzaW9uCnNlc3Npb25ib3hfRWxlbWVudCA8LQogIHJlbURyJGZpbmRFbGVtZW50KHVzaW5nPSJjc3MiLCAgICAgICAgICAgICAgICAgICAgIHZhbHVlPSIjdmlld1xcOl9pZDFcXDpfaWQyXFw6dHh0U2VzcyIpCiMgVHlwZSB0aGUgc2Vzc2lvbiBudW1iZXIgaW50byBpdApzZXNzaW9uYm94X0VsZW1lbnQkc2VuZEtleXNUb0VsZW1lbnQobGlzdCgiNjgiKSkKCiMgRmluZCB0aGUgdGV4dCBib3ggZm9yIGZ1bGwgdGV4dCBzZWFyY2gKZnVsbHRleHRib3hfRWxlbWVudCA8LQogIHJlbURyJGZpbmRFbGVtZW50KHVzaW5nPSJjc3MiLCAgICAgICAgICAgICAgICAgICAgICB2YWx1ZT0iI3ZpZXdcXDpfaWQxXFw6X2lkMlxcOnR4dEZUU3JjaCIpCiMgVHlwZSB0aGUgc2VhcmNoIHRlcm0gaW4KZnVsbHRleHRib3hfRWxlbWVudCRzZW5kS2V5c1RvRWxlbWVudChsaXN0KCJtaWdyYXRpb24iKSkKYGBgCgpOb3cgeW91J3JlIGJyb3dzZXIgd2luZG93IHNob3VsZCBsb29rIGxpa2UgdGhpczoKCi0tLS0tLQoKPGNlbnRlcj4KYGBge3IsLCBvdXQud2lkdGg9IjcwJSIsICBvdXQuZXh0cmE9J3N0eWxlPSJiYWNrZ3JvdW5kLWNvbG9yOiAjOWVjZmY3OyBwYWRkaW5nOjEwcHg7ICBkaXNwbGF5OmlubGluZS1ibG9jayInLCBldmFsPVRSVUUsIGVjaG89RkFMU0V9CgppbmNsdWRlX2dyYXBoaWNzKCJpbWFnZXMtVU5zZWFyY2gvdGV4dGJveGVzLmpwZWciKQpgYGAKPC9jZW50ZXI+CgotLS0tLS0KCk5vdyBjbGljayBzZWFyY2guCgpgYGB7cn0KIyBGaW5kIHRoZSBzZWFyY2ggYnV0dG9uCnNlYXJjaGJ1dHRvbl9FbGVtZW50IDwtCiAgcmVtRHIkZmluZEVsZW1lbnQodXNpbmc9ImNzcyIsIHZhbHVlPSIjdmlld1xcOl9pZDFcXDpfaWQyXFw6YnRuUmVmaW5lIikKIyBDbGljayBpdApzZWFyY2hidXR0b25fRWxlbWVudCRjbGlja0VsZW1lbnQoKQpgYGAKClRoYXQgZ2l2ZXMgdXMgc2VhcmNoIHJlc3VsdHMgYXQgdGhlIGJvdHRvbToKCi0tLS0tLQoKPGNlbnRlcj4KYGBge3IsLCBvdXQud2lkdGg9IjcwJSIsICBvdXQuZXh0cmE9J3N0eWxlPSJiYWNrZ3JvdW5kLWNvbG9yOiAjOWVjZmY3OyBwYWRkaW5nOjEwcHg7ICBkaXNwbGF5OmlubGluZS1ibG9jayInLCBldmFsPVRSVUUsIGVjaG89RkFMU0V9CgppbmNsdWRlX2dyYXBoaWNzKCJpbWFnZXMtVU5zZWFyY2gvc2VhcmNocmVzdWx0cy5qcGVnIikKYGBgCjwvY2VudGVyCgpUaGVyZSBhcmUgMzU3IGRvY3VtZW50cyBtZWV0aW5nIG91ciBxdWVyeSwgd2l0aCAyMCBvbiBlYWNoIHBhZ2UuCgpUbyBnZXQgdG8gdGhlIEVuZ2xpc2ggcGRmIChvbmx5KSBmb3IgYW55IG9uZSByZXN1bHQsIHdlIG5lZWQgdG8gcmV2ZWFsIHRoZSBsaW5rIHRvIGl0IHVuZGVyIHRoYXQgcmVzdWx0J3MgIkRldGFpbHMiIGJ1dHRvbi4KClNvIHdlIGZpbmQgYWxsIDIwIG9mIHRob3NlLCBjbGljayB0aGVtIGFsbCwgYW5kIHRoZW4gY2xpY2sgb24gdGhlIEVuZ2xpc2ggcGRmIGRvd25sb2FkIGxpbmtzIHRoYXQgYXJlIHJldmVhbGVkLgoKKEZvciBpbGx1c3RyYXRpb24gcHVycG9zZXMsIEkgb25seSBkb3dubG9hZCB0aHJlZSBoZXJlLikKCi0tLS0tLQpgYGB7cn0KIyBGaW5kIHRoZSAyMCAiRGV0YWlscyIgYXJyb3dzCkRldGFpbHNfRWxlbWVudHMgPC0gcmVtRHIkZmluZEVsZW1lbnRzKHVzaW5nPSJjc3MiLCB2YWx1ZT0iLm9kc0ljb246bnRoLWNoaWxkKDEpIikKIyBjbGljayBhbGwgb2YgdGhlbQpmb3IgKGkgaW4gMToyMCkgeyBEZXRhaWxzX0VsZW1lbnRzW1tpXV0kY2xpY2tFbGVtZW50KCkgfQojc2FwcGx5KERldGFpbHNfRWxlbWVudHMsIGZ1bmN0aW9uKHgpIHggJT4lIGNsaWNrRWxlbWVudCkKCiMgRmluZCB0aGUgMjAgRW5nbGlzaCBwZGYgbGlua3MKUERGX0VsZW1lbnRzIDwtIHJlbURyJGZpbmRFbGVtZW50cyh1c2luZz0iY3NzIiwgdmFsdWU9Ii5jb2wteHMtNDpudGgtY2hpbGQoMykgLmZhLWZpbGUtcGRmLW8iKQoKZm9yIChpIGluIDE6MykgeyAjSnVzdCBnZXQgdGhyZWUKICBQREZfRWxlbWVudHNbW2ldXSRjbGlja0VsZW1lbnQoKQogIFN5cy5zbGVlcCgxMCkKfQpgYGAKClRoZSB0aHJlZSBmaWxlcyBzaG91bGQgYmUgaW4geW91ciBkZWZhdWx0IERvd25sb2FkcyBmb2xkZXIuIChUaGlzIGNhbiBwcm9iYWJseSBiZSBjaGFuZ2VkIHdpdGggYW5vdGhlciBjb21tYW5kIGluIHRoZSBGaXJlZm94IHByb2ZpbGUuKQoKV2UncmUgZG9uZSB3aXRoIHRob3NlIDIwLCBzbyBnbyB0byB0aGUgbmV4dCBwYWdlLgpgYGB7cn0KbmV4dHBhZ2VfRWxlbWVudCA8LQogIHJlbURyJGZpbmRFbGVtZW50KHVzaW5nPSJjc3MiLCB2YWx1ZT0iLmRpc2FibGVkKyBsaSBhIikKbmV4dHBhZ2VfRWxlbWVudCRjbGlja0VsZW1lbnQoKQpgYGAKClJlcGVhdCB0aGUgZmlsZSBkb3dubG9hZCBvbiBlYWNoIHBhZ2UgYW5kIGNsaWNrIHRvIHRoZSBuZXh0IHVudGlsIHlvdSdyZSBkb25lLgoKQ2xvc2UgeW91ciBicm93c2VyL3NlcnZlci4KYGBge3J9Cm15ZHJpdmVyJHNlcnZlciRzdG9wKCkKYGBgCgpUbyBkbyB0aGlzIHN5c3RlbWF0aWNhbGx5LCB5b3Ugd291bGQgaGF2ZSB0byBwdXQgYWxsIG9mIHRoYXQgaW4gYW4gYXV0b21hdGVkIGxvb3AgLS0tIGxvb3Bpbmcgb3ZlciBzZXNzaW9ucyB3YW50ZWQgZm9yIG5ldyBzZWFyY2hlcywgd2l0aGluIHRoYXQgbG9vcGluZyBvdmVyIHBhZ2VzIG9mIHRoZSBzZWFyY2ggcmVzdWx0cywgYW5kIHdpdGhpbiB0aGF0IGxvb3Bpbmcgb3ZlciBkb2N1bWVudCBsaW5rcyB0byBkb3dubG9hZCAtLSB3aGljaCBJICJsZWF2ZSBhcyBhbiBleGVyY2lzZS4iCgpUaGlzIGlzIFZFUlkgdGltZS1jb25zdW1pbmcgYW5kIGFza3MgYSBsb3Qgb2YgdGhlIHNlcnZlci4gSSB3b3VsZCB2ZXJ5IGNsb3NlbHkgY29uc2lkZXIgd2hldGhlciBJIHJlYWxseSBuZWVkZWQgYWxsIHRoZSBkb2N1bWVudHMgdGhpcyBicm9hZCBzZWFyY2ggcmV0dXJucyBiZWZvcmUgSSBwcm9jZWVkZWQu