0

I'm creating a Chrome extension that should interact with the user selection in the Microsoft Word Online documents - add new highlighting instead of the natural selection highlighting and then remove it. The problem is that I'm not able to get the user selection: the response for the window.getSelection() returns the result like the selection is empty.

Here are files from my extension:

manifest.json

{
    "manifest_version": 2,
    "name": "The extension name",
    "version": "1.0",
    "description": "This extension description",

    "browser_action": {
        "default_icon": "icon.png",
        "default_popup": "popup.html"
    },

    "icons": {
        "128": "icon.png"
    },

    "content_scripts": [{
        "matches": ["<all_urls>"],
        "js": ["content_script.js"],
        "run_at": "document_end",
        "all_frames": true
    }],

    "permissions": ["activeTab"]
}

popup.html

<!doctype html>
<html>
<head>
    <script src="popup.js"></script>
</head>
<body>
    <div id="wrapper">
        <form id="settings" name="settings">
            <div id="apply" class="form-row">
                <input type="submit" name="apply" value="Apply"/>
            </div>
        </form>
    </div>
</body>
</html>

popup.js

document.addEventListener('DOMContentLoaded', function() {
    document.getElementById("settings").addEventListener("submit", function (e) {
        e.preventDefault();

        chrome.tabs.executeScript(null, {file: 'toolbar.js'}, function() {
            chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
                chrome.tabs.sendMessage(tabs[0].id, {});
            });
        );
    }, false);
});

toolbar.js

function showToolbar() {
    var dom_body = document.getElementsByTagName('body')[0];
    var tb_wrapper = document.createElement('div');
    tb_wrapper.id = "toolbar_wrapper";

    var tb_toolbar_play = document.createElement('button');
    tb_toolbar_play.id = "toolbar_play";
    tb_toolbar_play.title = "Play";
    tb_toolbar_play.value = "Play";

    tb_wrapper.appendChild(tb_toolbar_play);
    dom_body.appendChild(tb_wrapper);
}

showToolbar();

content_script.js

function playButtonOnClickEventListener(request, sender, sendResponse) {
    var toolbar = document.getElementById("toolbar_wrapper");

    if (toolbar !== null) {
        var toolbar_play_button = document.getElementById("toolbar_play");
        toolbar_play_button.addEventListener("click", function (e) {
            var selection = window.getSelection();
            console.log(selection);
        });
    }

    sendResponse({data: "response", success: true});
}

chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
    playButtonOnClickEventListener(request, sender, sendResponse);
});

So, what I want to see in the Chrome Developer tools after the console.log(selection) executes:
the expected result in the Chrome Developer tools

What I actually get:
the actual result in the Chrome Developer tools

P.S. The extension works perfectly with Google Docs.

Makyen
  • 31,849
  • 12
  • 86
  • 121
lujewwy
  • 9
  • 5
  • Please [edit] the question to be on-topic: include a [mcve] that duplicates the problem. For Chrome extensions or Firefox WebExtensions this almost always means including your *manifest.json* and some of the background, content, and/or popup scripts/HTML. Questions seeking debugging help ("why isn't this code working the way I want?") must include: (1) the desired behavior, (2) a specific problem or error and (3) the shortest code necessary to reproduce it *in the question itself*. Please also see: [What topics can I ask about here?](http://stackoverflow.com/help/on-topic), and [ask]. – Makyen Apr 19 '17 at 15:22
  • The reason that a [mcve] is required is that *we want to help*. It is **much** easier to help if we don't have to recreate any of the code needed to duplicate the problem. This is code that you already have. So, please help us to help you and provide a *complete* [mcve] that duplicates the problem. Without a [mcve], the amount of effort required to even begin to help you is **much** higher, which *significantly* reduces the number of people willing/able to help you. Even if we put out the extra effort, we have to **guess** at significant portions of what your problem might be. – Makyen Apr 19 '17 at 15:38
  • Is this for all selections regardless of where they are on the page, or only some selections? – Makyen Apr 20 '17 at 15:38
  • BTW: `document.getElementsByTagName('body')[0]` could be replaced by `document.body`. – Makyen Apr 20 '17 at 15:39
  • In fact, the function could be: `function showToolbar() {document.body.insertAdjacentHTML('beforeend','
    ');}`. There's nothing wrong with creating elements individually. You should do so when the values are dynamic, particularly if not sourced entirely from your code (e.g. user input, or from the net). [`.insertAdjacentHTML()`](https://developer.mozilla.org/en-US/docs/Web/API/Element/insertAdjacentHTML) tends to be faster and allows you to code it as HTML text, which may/may not be more maintainable.
    – Makyen Apr 20 '17 at 15:49
  • @Makyen, thank you for the advices! And the issue applies for any selection, regardless of where it is on the page. – lujewwy Apr 21 '17 at 05:23

2 Answers2

0

I'm not sure how the Word Online editor is built or why you can't use window.getSelection(), but one thing I noticed immediately is that when you select text in it, the editor gives that text a Selected class. So maybe you could take advantage of that somehow? Override the style of the relevant elements?

Possibly you could use a MutationObserver and look for this class name being added to elements. You would have to do some experimenting to see if this works for your needs, and obviously if Microsoft change the class name or behaviour, your extension breaks.

Try selecting some text and putting

document.getElementsByClassName("Selected")[0].innerHTML

in the console.

enter image description here

Karl Reid
  • 2,147
  • 1
  • 10
  • 16
  • Thank you for your response, but the problem is that the content of the "sdx_ow_iframe" (highlighted with light-red on your screenshot) is not accessible from my extension (content_script.js). – lujewwy Apr 20 '17 at 06:29
  • Are you using the `all_frames` option with your content scripts? If not, they will only run in the top level, so that would explain why you can't access the content of that frame. https://developer.chrome.com/extensions/content_scripts (look for `all_frames`) – Karl Reid Apr 20 '17 at 10:56
  • Ah, sorry, I see you edited the question to include your code, and you are using that option. In that case I'm not sure then. – Karl Reid Apr 20 '17 at 10:57
0

Here's the solution of the issue. Hope it can be useful for somebody once.

Currently I see the whole picture differently, so I changed the number of files and its content.

manifest.json

{
    "manifest_version": 2,

    "version": "1.1.1",

    "name": "Test name",

    "description": "Test description",

    "browser_action": {
        "default_icon": "icon.png"
    },

    "icons": {
        "128": "icon.png"
    },

    "content_scripts": [
        {
            "matches": ["<all_urls>"],
            "js": ["content_script.js"],
            "run_at": "document_end",
            "all_frames": true
        }
    ],

    "permissions": ["activeTab"]
}

content_script.js

//Word Online pages contain several different iframes, so, in order 
//to find the one where the text that we are going to work with 
//is placed, we have to use the Chrome Runtime API and its 
//"sendMessage" method that works in every frame.
//https://developer.chrome.com/apps/runtime#method-sendMessage

chrome.runtime.sendMessage("", function() {
    if (checkFrameLocation()) {
        showToolbar();
    }
});

function checkFrameLocation() {
    //So as I work with documents in OneDrive (onedrive.live.com) 
    //and in Sharepoint (<USER_NAME>-my.shrepoint.com), I found out 
    //that I need iframes with these URLs only.

    var trusted_urls = [
        "https://euc-word-edit.officeapps.live.com",
        "https://word-edit.officeapps.live.com",
        "nl1-word-edit.officeapps.live.com"
    ];

    var result = false;

    for (var i = 0; i < trusted_urls.length; i++) {
        if (window.location.href.indexOf(trusted_urls[i]) > -1) {
            result = true;
        }
    }

    return result;
}

function showToolbar() {
    var dom_body = document.body;

    var tb_wrapper = document.createElement('div');
    tb_wrapper.id = "toolbar_wrapper";
    tb_wrapper.style.position = "absolute";
    tb_wrapper.style.top = "200px";
    tb_wrapper.style.left = "200px";
    tb_wrapper.style.backgroundColor = "#ffffff";
    tb_wrapper.style.height = "30px";
    tb_wrapper.style.width = "50px";

    var tb_play = document.createElement('button');
    tb_play.id = "toolbar_play_button";
    tb_play.title = "Play";
    tb_play.innerHTML = "&#9658;";
    tb_play.style.height = "100%";
    tb_play.style.width = "100%";
    tb_play.style.lineHeight = "12px";

    tb_play.addEventListener("click", function() {
        playButtonOnClickEventListener();
    });

    tb_wrapper.appendChild(tb_play);
    dom_body.appendChild(tb_wrapper);
}

function playButtonOnClickEventListener() {
    //Now we can use the window selection object

    var window_selection = window.getSelection();
    console.log(window_selection);

    //Also, Word Online adds special class name to every selected element
    //in the document. So, this is another way to get the selected text.

    var elements_selection = document.getElementsByClassName("Selected");
    console.log(elements_selection);
}

And here's the proof screenshot:

There are results of accessing the selected text in the Word Online document

lujewwy
  • 9
  • 5