2

Basically I made a bookmarklet, and I'd like it to run when a certain wildcarded URL is opened. For some reasons it won't simply run as javascript in a chrome extension, and I'm tired of trying.

What I think could work is to make an extension that has a content_script for the specified page(s) (which allows a wildcard via match), and somehow make it do the same thing that would be done if the user clicked the bookmarklet in the bookmarks bar.

However, I do not know how to make this.

One thing to note is that I need it to access the page's global scope, i.e., break out of the extension sandbox (which is possible, and has been confirmed to be possible by design in the Chromium bug tracker).

So the question again is: how, from an content_script, do I "load the bookmarklet" (in other words, how to convert a bookmarklet to a Google Chrome extension). I have it in plain javascript too, if that could be of use.

This is the bookmarklet, in case somebody wants to test with it. It's meant to be used at my.deviantart.com/messages/* (but you need an account and messages in your inbox, to see the effect hover on top of a link to a "deviation", and it will show a tooltip with a thumbnail of it).

(Edit: Here's an extension attempt, posted in an answer's comments)

Camilo Martin
  • 37,236
  • 20
  • 111
  • 154

3 Answers3

1

You can make cross domain calls from a content script if you put a url in the permissions part of your manifest...
http://code.google.com/chrome/extensions/xhr.html

What it seemed to be choking on was the callback that you put in the request url and thats not needed so I took it out.
Here's a working version of your code....
Manifest

{
  "name": "dA Tooltip Thumbnail",
  "version": "1.0.0",
  "description": "What the name says.",
  "permissions": [
    "http://backend.deviantart.com/*"
  ],
  "icons": {
    "48" : "sample-48.png",
    "128" : "sample-128.png"
  },
  "content_scripts": [
    {
      "matches": ["http://my.deviantart.com/messages/*"],
      "js" : ["jquery-1.7.1.min.js","contentscript.js"]
    }
  ]
}

ContentScript

$(".mcb-title a:first-child").each(function() {
    var b=$(this).attr("href");
    null!=b.match(/https?:\/\/fav\.me\/.*|https?:\/\/.*\.deviantart\.com\/art.*/)&&"true"!=$(this).attr("da-message-preview-attached")&&$.getJSON("http://backend.deviantart.com/oembed?url="+encodeURIComponent(b),$.proxy(function(b) {
        $(this).addClass("da-message-preview").attr("rel",b.thumbnail_url).attr("da-message-preview-attached","true");
        $(this).hover(function(a) {
            window.daMessagePreviewTitle=this.title;
            this.title="";
            $("body").append('<p id="da-message-preview"><img src="'+this.rel+'"/></p>');
            $("#da-message-preview").css( {top:a.pageY-10+"px",left:a.pageX+30+"px",position:"absolute",border:"1px solid #666",background:"#EEE",padding:"5px",display:"none","-webkit-border-radius":"6px","-moz-border-radius":"6px","border-radius":"6px","-webkit-box-shadow":"0px 2px 8px #000","-moz-box-shadow":"0px 2px 8px #000","box-shadow":"0px 2px 8px #000","z-index":"123456"}).fadeIn("fast")
        },function() {
            $("#da-message-preview").remove()
        });
        $(this).mousemove(function(a) {
            $("#da-message-preview").css("top",a.pageY-10+"px").css("left",a.pageX+30+"px")
        })
    },this))

});  

The only error I noticed after the changes was it tries to get a url that gets a 404...
http://backend.deviantart.com/oembed?url=http%3A%2F%2Fnews.deviantart.com%2Farticle%2F143885%2F
...small error, Ill leave it up to you to get rid of that one ;).
OH, and I took out the timer stuff, is that really needed? Wont you be going to a different url when you click on a gallery?...because if you do then the content script will get reinjected (you may need to add more matches for that tho, didnt really look).

PAEz
  • 8,366
  • 2
  • 34
  • 27
  • PS Cool script. When you release it could you send me a note of where it is...just send me a PM at my dA account if thats cool.... http://paezz.deviantart.com/ and where did you learn about the dA api? Im not seeming to find any good info on it. – PAEz Jan 24 '12 at 08:27
  • I'm very happy you liked the script, I'll definitely send you a PM when I get it working! I also had a tough time finding about the badly-advertised dA API, and I even tried hacking around with the js of the site (painful and fruitless effort), until I found it, it's [here](http://www.deviantart.com/developers/oembed). Geez, it's my first bookmarklet, first greasemonkey script, and first google chrome extension, and it's been a while since I don't see Javascript, I'm so happy sombody likes it! – Camilo Martin Jan 24 '12 at 18:58
  • Now on the technical side, yes, the timer is needed, because folders are loaded via AJAX. It took me a while to figure out I'd need that. Also, it does not consume much CPU (I'd say hardly one percent of an old CPU) because half a second is a lot of time and it only does its work once (it adds an attribute for that). I'll give your script a spin! Hope it works! – Camilo Martin Jan 24 '12 at 19:03
  • By the way, is this my impression, or you un-minified the bookmarklet? I posted a link to a fully-commented attempt at an extension in another answer's comments, I'll add it to the main post (my bad for not doing so before). [It's this one](http://sta.sh/01vn95p8q0v2). – Camilo Martin Jan 24 '12 at 19:16
  • Your script worked. I'm really astonished that the whole problem is that I was using the JSONP format, and somehow [JSON with Padding](http://en.wikipedia.org/wiki/JSONP) does not work well in Chrome's "[Isolated World](http://www.youtube.com/watch?v=laLudeUmXHM)". [`$.getJSON` uses JSONP](http://api.jquery.com/jQuery.getJSON/#jsonp) if you include a string like "callback=?" in the request URL. Well, problem solved, but really, it's frustrating that my script was giving this error for no reason. [Here is what it looked like](http://i.imgur.com/Qo2rd.png) (the names are generated by jQuery). – Camilo Martin Jan 24 '12 at 19:47
  • 1
    Another thing you wanted was to have options for your content script without having a background page, well I figured out how to do that today..... http://forum.valorsolo.com/viewtopic.php?f=36&t=375 ..gotta jump through a couple of hoops, but it works ;) – PAEz Mar 04 '12 at 11:04
  • Please do post your solution [here](http://stackoverflow.com/questions/9033873/options-enabled-content-script-chrome-extension-without-background-page) so I can accept it. I had thought about using an iframe, for some reason I dismissed the idea as being too funny. Bookmarked the link, I did not test it but I assume it works by the way it looks. You're awesome, I had given up by now! – Camilo Martin Mar 06 '12 at 06:45
  • 1
    Posted. I come from the days when using a frame was considered really bad practice, but Ive been reading about why iframes are usable and totally cool these days. And whilst playing with them it occurred to me that this might work. Pitty we have to jump through hoops to do it tho, really wish extensions had a settings api that allowed settings to be synced (stored in the google cloud) and accessible from content scripts (Without a background page). – PAEz Mar 06 '12 at 08:10
  • Nice to know that iframes are not seen with bad eyes anymore, this kind of use is pretty cool! It's indeed a pity that it's necessary, but I hate the idea of Chrome wasting RAM when my extension is not even in use. There is however an [aptly-named task force](http://code.google.com/p/chromium/issues/list?q=label:TaskForce-BackgroundPagesMustDie) that I hope gets somewhere. Also, in the (hopefully near) future, You'll be able to use [`chrome.storage.sync`](http://code.google.com/chrome/extensions/trunk/storage.html#property-sync) to sync options (hopefully from content scripts too). – Camilo Martin Mar 06 '12 at 19:29
0

There is almost no work required in converting from a bookmarklet to a Chrome extension (provided the bookmarklet only accessess DOM elements - a criterion with which your extension appears to comply). Simply paste the JavaScript into your extension's content_script.js.

Note, though, that your bookmarklet uses jQuery. You'll have to embed that in your content script, as well. See here for how to do that.

One other note. You don't need to exploit some sort of bug in order to "break out" of the extension; by design, Chrome extensions are allowed access to DOM elements of the page, but nothing else inside of the JavaScript namespace. In other words, if the page has loaded some super secret variables into var bob = 'My secret!!!1', your extension could not access bob and read its value. On the other hand, if bob's value were loaded into a span tag, your extension could find that tag and read it, since it is part of the DOM.

cheeken
  • 33,663
  • 4
  • 35
  • 42
  • `There is almost no work required in converting from a bookmarklet to a Chrome extension` I have a bookmarklet that works as expected since some 5-6 hours ago, and I couldn't make an extension out of it, no matter what I tried. [Check it out if you want](http://sta.sh/01vn95p8q0v2). The current error it gives, basically, is that the callback function that jQuery uses to load JSONP (padded JSON) is not found when it's called (I can't make sense out of it, and I just gave up if I can somehow make my working bookmarklet auto-load). I really just want any hack that will make it work, even if ugly. – Camilo Martin Jan 23 '12 at 17:42
  • Did you include `jquery.js` in your extension? If you could post the actual error message, that would be helpful. – cheeken Jan 23 '12 at 18:32
  • Yes I did. [here's what it looks like](http://i.imgur.com/Qo2rd.png). Soon, the Google Chrome logo will become a trollface. – Camilo Martin Jan 23 '12 at 19:12
  • @CamiloMartin I loaded your extension and I don't get any errors. The code is firing, too. I can't confirm the behavior is what you expect since I don't know what it does, but I don't get any console errors. Running latest Chrome 17. – cheeken Jan 23 '12 at 19:20
  • I unloaded the extension and reloaded it, it gives me those errors. I don't know how you've ran it (I think it matters to know how you tested it outside of it's intended page), but basically it fails to fire the jQuery-created randomly-named callback function fired by loading JSONP. How/where did you try it? What code fired? – Camilo Martin Jan 23 '12 at 19:35
  • By the way, just to clear that up, what the script does is to check every half a second for certain anchors who point to certain URLs, get a thumbnail image of these URLs, and attach a tooltip thingy (only if it didn't already). There are so many errors because it was meant to only try to attach these once (but never manages to because jQuery can't load the JSONP). I can't use plain JSON because of cross-domain policy. – Camilo Martin Jan 23 '12 at 19:39
  • I added an `alert` to the content script. Then I browsed to my deviantart messages, where the alert appeared, and I observed no errors in the console. At that point, I stopped investigating. I did note, however, that I only received one alert, despite your `setTimeout`. Not sure what to make of that. – cheeken Jan 23 '12 at 19:44
  • Where exactly did you add the alert? The problem is in the JSONP callback (`$.getJSON` uses JSONP depending on the how the URL ends), does it get called for you? I'm on Chrome 16. It should be called once for every link to a deviation, and again if you load a folder with other deviations (that's the point of the `setTimeout`). Also, please use the bookmarklet to see the intended effect. – Camilo Martin Jan 23 '12 at 19:55
-1

I think what you're looking for is Message Passing:

http://code.google.com/chrome/extensions/messaging.html

With it you can pass events from your content_script to your background script. Within the background page you'll have all the Chrome Extension capabilities at your disposal.

Rick van Mook
  • 2,065
  • 1
  • 12
  • 16
  • I do not understand how this does apply, could you please explain? I have no background script, and what I have works if manually clicked as a bookmarklet. I just need to "click the bookmarklet" via code. – Camilo Martin Jan 23 '12 at 17:45
  • It's ok, but if you have any idea, please tell me. – Camilo Martin Jan 23 '12 at 20:59