Message passing between popover and global page in safari extension without using globalPage.contentwindow - safari-extension

Is there a way to pass messages from the popover to global page using the dispatchMethod() instead of calling the global page's functions using safari.extension.globalPage.contentWindow.
Currently i use a dynamically created iframe inside the web page to simulate a popover. This communicates with the global page using Safari's message passing. So i want to support this as well as the new popover in the later Safari versions.
Message passing between the popover and the global page will help me reuse the code.
Thanks

It doesn't look like there is a way for a popover to dispatch a message to the global page, or vice versa, using dispatchMessage. However, you could use the HTML5 standard window.postMessage to do the equivalent, although then you could not reuse your existing code without some modification.
To use postMessage from the popover, you would do something like this:
var gw = safari.extension.globalPage.contentWindow;
gw.postMessage("hello there", window.location.origin);
And to receive it in the global page:
window.addEventListener('message', function (msg) {
if (msg.origin == window.location.origin) {
msg.source.postMessage("got your message", window.location.origin);
doSomethingWithMessageData(msg.data);
}
}, false);
This messaging protocol is similar enough to the extension-specific one that you could probably reuse most of your existing code, with just a thin abstraction layer added.

Related

Aurelia - dynamically create custom element in a view-model

I have an Aurelia app where a user can click on a button and create a new tab. The tab and its content (a custom element) do not exist on the page before the user clicks the button. I am generating the HTML for the content at runtime (via Javascript) in my view model.
I keep seeing mention of using the template engine's compose or enhance functions, but neither are working for me. I don't know how I would use the <compose> element (in my HTML) since I am creating the element based on the user clicking a button.
My thought was that the button has a click.delegate to a function that does ultimately does something like
const customElement = document.createElement('custom-element');
parentElement.appendChild(customElement);
const view = this.templatingEngine.enhance({
element : customElement,
container : this.container, // injected
resources : this.viewResources, // injected
bindingContext: {
attrOne: this.foo,
attrTwo: this.bar,
}
});
view.attached();
But all this does is create an HTML element <custom-element></custom-element> without actually binding any attributes to it.
How can I create a custom element analogous to <custom-element attr-one.bind="foo" attr-two.bind="bar"></custom-element> but via Javascript?
As you pointed out in your own answer, it's the missing resources that caused the issue. One solution is to register it globally. That is not always the desired behavior though, as sometimes you want to lazily load the resources and enhance some lazy piece of HTML. Enhance API accepts an option for the resources that you want to compile the view with. So you can do this:
.enhance({
resources: new ViewResources(myGlobalResources) // alter the view resources here
})
for the view resources, if you want to get it from a particular custom element, you can hook into the created lifecycle and get it, or you can inject the container and retrieve it via container.get(ViewResources)
I found the problem =\ I had to make my custom element a global resource.

How to inject CSS into webkit?

On Linux I'm creating a webkit window which needs to display a certain URL.
I'm doing that like the following:
GtkWidget *main_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
// Create a browser instance
WebKitWebView *webView = WEBKIT_WEB_VIEW(webkit_web_view_new());
// Put the browser area into the main window
gtk_container_add(GTK_CONTAINER(main_window), GTK_WIDGET(webView));
// Load a web page into the browser instance
webkit_web_view_load_uri(webView, "http://example.com");
// Make sure that when the browser area becomes visible, it will get mouse
// and keyboard events
gtk_widget_grab_focus(GTK_WIDGET(webView));
// Show the result
gtk_window_set_default_size(GTK_WINDOW(main_window), 800, 600);
gtk_widget_show_all(main_window);
However, I need to inject some CSS into this to hide a certain checkbox.
How do I inject CSS into the DOM.
I see that I can get the dom like
WebKitDOMDocument *dom = webkit_web_view_get_dom_document(webView);
But from here I can't see how to inject the CSS.
It sounds like the webkit_web_view_run_javascript() answer was a good solution to your specific problem, since you only needed to hide one checkbox.
To answer the general problem of how to inject arbitrary CSS: if you're using a recent version of WebKitGTK+, create a WebKitUserContentManager, call webkit_user_content_manager_add_stylesheet(), and then pass the WebKitUserContentManager when creating your WebKitWebView, either using webkit_web_view_new_with_user_content_manager() or by using g_object_new() manually if you need to set multiple construct-only properties.
Unrelated warning: webkit_web_view_get_dom_document() was removed in WebKitGTK+ 2.6. (The DOM API is only accessible via web process extensions nowadays.) You are using an old, insecure version of WebKitGTK+!
Its not clear which Webkit GTK version you are using, however concepts essentially remain same for both versions. For webkit version 2, its slightly more complicated as DOM manipulation is done on extension side.
You need to reach to the desired element - either by id e.webkit-dom-document-get-element-by-id or by name. This will return you instance of WebElement. If you use by name call, please be ware that there could be multiple elements with same name
From here you can either set the style by setting appropriate style attribute webkit_dom_element_set_attribute or other variations that can deal with styles and css rules.
Or you can take easy option and just execute the javascript that does the same thing by calling webkit_web_view_run_javascript

Win8 JS App: How can one prevent backward navigation? Can't set WinJS.Navigation.canGoBack

Fairly new to developing for Windows 8, I'm working on an app that has a rather flat model. I have looked and looked, but can't seem to find a clear answer on how to set a WinJS page to prevent backward navigation. I have tried digging into the API, but it doesn't say anything on the matter.
The code I'm attempting to use is
WinJS.Navigation.canGoBack = false;
No luck, it keeps complaining about the property being read only, however, there are no setter methods to change it.
Thanks ahead of time,
~Sean
canGoBack does only have a getter (defined in base.js), and it reflects the absence or presence of the backstack; namely nav.history.backstack.
The appearance of the button itself is controlled by the disabled attribute on the associated button DOM object, which in turn is part of a CSS selector controlling visibility. So if you do tinker with the display of the Back button yourself be aware that the navigation plumbing is doing the same.
Setting the backstack explicitly is possible; there's a sample the Navigation and Navigation History Sample that includes restoring a history as well as preventing navigation using beforenavigate, with the following code:
// in ready
WinJS.Navigation.addEventListener("beforenavigate", this.beforenavigate);
//
beforenavigate: function (eventObject) {
// This function gives you a chance to veto navigation. This demonstrates that capability
if (this.shouldPreventNavigation) {
WinJS.log && WinJS.log("Navigation to " + eventObject.detail.location + " was prevented", "sample", "status");
eventObject.preventDefault();
}
},
You can't change canGoBack, but you can disable the button to hide it and free the history stack.
// disabling and hiding backbutton
document.querySelector(".win-backbutton").disabled = true;
// freeing navigation stack
WinJS.Navigation.history.backStack = [];
This will prevent going backward and still allow going forward.
So lots of searching and attempting different methods of disabling the Back Button, finally found a decent solution. It has been adapted from another stackoverflow question.
Original algorithm: How to Get Element By Class in JavaScript?
MY SOLUTION
At the beginning of a fragment page, right as the page definition starts declaring the ready: function, I used an adapted version of the above algorithm and used the resulting element selection to set the disabled attribute.
// Retrieve Generated Back Button
var elems = document.getElementsByTagName('*'), i;
for (i in elems)
{
if((" "+elems[i].className+" ").indexOf("win-backbutton") > -1)
{
var d = elems[i];
}
}
// Disable the back button
d.setAttribute("disabled", "disabled");
The code gets all elements from the page's DOM and filters it for the generated back button. When the proper element is found, it is assigned to a variable and from there we can set the disabled property.
I couldn't find a lot of documentation on working around the default navigation in a WinJS Navigation app, so here are some methods that failed (for reference purposes):
Getting the element by class and setting | May have failed from doing it wrong, as I have little experience with HTML and javascript.
Using the above method, but setting the attribute within the for loop breaks the app and causes it to freeze for unknown reasons.
Setting the attribute in the default.js before the navigation is finished. | The javascript calls would fail to recognize either methods called or DOM elements, presumably due to initialization state of the page.
There were a few others, but I think there must be a better way to go about retrieving the element after a page loads. If anyone can enlighten me, I would be most grateful.
~Sean R.

How to replace jquery's live for elements that don't exist when the page is loaded

I have seen numerous advice on stackexchange and all over the web suggesting that I not use jquery's live function. And at this point, it is deprecated, so I'd like to get rid of it. Also I am trying to keep my javascript in one place(unobtrusive)--so I'm not putting it at the bottom of the page. I am unclear though on how to rewrite the code to avoid live for elements that don't yet exist on the page.
Within the javascript file for my site I have something like this:
$(function() {
$('button.test').live('click', function(){
alert('test');
});
});
.on( doesn't work since the element doesn't exist yet.
The button is in a page I load in a colorbox pop-up modal window. I'm not sure exactly where that colorbox window sits in the DOM but I think it is near the top.
I could use delegate and attach this to the document--but isn't the whole point of not using live to avoid this?
What is the best way to get rid of live in this case?
You can use .on() - http://api.jquery.com/on/
$(document).on("click", "button.test", function() {
alert('test');
});
If you use live() you can use die().
You can also use on() and off().
They do about the same thing but its recomended to use on.
I ended up avoiding both live and an on attached at the document level. Here's how:
Wrap all of the jquery code specific to objects in a page which loads in the colorbox window in the function like so:
function cboxready(){
...
}
The code wrapped in this function can attach directly to the objects (instead of attaching at the document level) since it will only get run once the colorbox window is open.
Then just call this function using colorbox's callback when you attach the colorbox, like so:
$('a.cbox').colorbox({
onComplete:function(){ cboxready(); }
});

Chrome Extension Dev: Updating javascript variables in background.html

I have a working extension, and now I'm trying to add some options/features!
I have a background page which keeps track of the "state" of each tab (per tab javascript variables that hold options/settings). Now I want to have a popup.html file which will have 1 option, a time slider. I'm not concerned with the slider or any html/css. My issue is that I'm unsure of how to communicate the new setting to the background page.
My background page contains some code like this:
chrome.browserAction.onClicked.addListener(function(tab) {
Init(tab);
});
...
function Init(tab)
{
chrome.tabs.sendRequest(tab.id,
{
'TurnOffTimer': false,
'TimerIsOn': TimerIsOn,
'Interval': Interval,
'RefreshRate': RefreshRate
}, responseCallback);
}
TimerIsOn, Interval, and RefreshRate are javascript variables on the same page.
What I need to know is how, once the slider (which is declared in the popup.html) is set and the user clicks 'ok', the timer value can be sent to background.html to be stored in the appropriate javascript variable, and the extension functionality can be updated. I can create another function called Update which will take this updated value and run, but I need to know how I can call the Update function from popup.html if it's declared in background.html.
Hopefully I'm making sense but if clarification is necessary, feel free to ask.
You can directly access background page's window object from popup with chrome.extension.getBackgroundPage(). So to change some var in bg page from popup you can just call:
chrome.extension.getBackgroundPage().Interval = 5;
You would need to change your extension a bit, as once you have popup attached to browser action button, chrome.browserAction.onClicked listener won't be firing anymore. You would need to call Init from popup manually in a similar way:
chrome.extension.getBackgroundPage().Init();
And inside Init() manually get selected tab id, as it won't be passed anymore.