Blockly: Black background in Safari and Edge

Created on 25 Jan 2017  路  21Comments  路  Source: google/blockly

Hi,

I'm building application in Angular 2 and implemented latest version of Blockly into it. Everything works perfectly in Chrome, but in Safari I'm seeing black background on .blocklySvg, and controls sprites are also being displayed wrong (seems like CSS bug). I use untouched blockly_compressed.js in vendors.js bundle and custom blocks implemented as directives, which are being built in separate bundle (app.js).

I tried to force background with CSS but nothing worked. I also tried to move toolbox outside of the injection div, also didn't help. Seems like bug in the source

Safari version 10.0.3 (12602.4.8.)
Chrome Version 55.0.2883.95 (64-bit)

See the enclosed screenshots
chrome
safari
screen shot 2017-01-25 at 17 31 40
screen shot 2017-01-25 at 17 32 19

bug

All 21 comments

Are you importing other CSS? What does the console tell you it thinks the background color is? Does Chrome's console tell you something different from Safari's?

Note that I have 0 experience with angular :-/

It does look like you have 4 copies of the whole sprite sheet, 1 for each control (trash, zoom in, etc.). Another thing to try besides digging around in your css definitions would be to toggle controls on and off and play with your grid to see if that changes anything.
I would play with the following:

trashcan: try setting to false
grid: try removing it
zoom: try removing it

I tried that. If I look in the developer console, I see the same colours in chrome and safari.
The only thing which I can change from external CSS is this:

.blocklyMainBackground {
// Ugly hack to fix black safari background
opacity: 0 !important;
}

The reason why there are 4 sprites - is that there are actually 4 controls - but in safari the proper background position and size are not applied, that why the whole sprite is displayed, instead of only the correct subpart of it.

It seems that this is definitely related to some CSS which is not loaded correctly - but as all CSS rules are defined in the blockly JS library, I have zero control over it.

Does changing the .blocklyMainBackground rule like that actually fix the background?
The .blocklySvg class on the inside the injection div is relevant ( Blockly's default css specifies a background-color: #fff. ) The fill on the rect with class .blocklyMainBackground is another relevant thing that comes to mind. Did you try turning off the grid to see if the background is still black?

Yes, it does seem like the CSS is loading in an odd way or there are some rule conflicts with a file outside of blockly. I'm debugging blind though since I don't see this problem in safari on our demos (e.g. https://blockly-demo.appspot.com/static/demos/code/index.html). If this page is hosted somewhere, I can take a look at the html and see if I spot anything.

It only hides the black colour (makes it transparent - hence white).
The demos works OK in Safari:

I created working version on my server, so here is the link:

http://blockly.dataworkers.eu/parser/classifiers

I faced the same problems in Safari and Edge:
The grid is black and the sprite duplicates 4 times.
Did you find any solutions ?

Hi,

I also face the same problem in Edge, even in newest version of angular. I reported this issue also in angular repo (https://github.com/angular/angular/issues/14843)

It has been updated with tag core&compiler bug, but no updates whatsoever after that :)

Thank You

This happens because you have 'base href="/"' tag in your AppComponent.html.

That tag influences how svg processes some style urls (which is normal behavior), but urls generated by GoogleBlockly do not need that tag, it is needed only for angular to work, and so some svg style urls become invalid.

In Angular 2+, you can inject the base path in your app module instead of using the tag.

import { APP_BASE_HREF } from '@angular/common';

@NgModule({
providers: [{
provide: APP_BASE_HREF,
useValue: '/'
}]
})
export class AppModule { }

Don't forget to remove 'base href="/"' tag from your AppComponent.html.

https://angular.io/docs/ts/latest/api/common/index/APP_BASE_HREF-let.html

That solves the problem. But creates another one with deep routing in Angular (deep direct links like www.somesite.com/entity/2 stop working)

Proper solution would be (finally):

  1. Add this function to Blockly sources
/**
 * Convert local uri to global uri, required for svg to work correctly when page contains <base> tag
 * Fixes Safari svg rendering issues (black background, misplaced zoom/trash controls etc)
 * @param {string} localUri local uri string
 * local uri example: "url(#blocklyTrashBodyClipPath837493)"
 * @return {!string} global uri
 */
Blockly.convertToGlobalUri = function (localUri) {
  var baseUrl = window.location.href.replace(window.location.hash, "");
  var idx = localUri.indexOf("url(");
  return localUri.slice(0, idx + 4) + baseUrl + localUri.slice(idx + 4);
};
  1. In all places where you see 'url(#' inside blockly sources, use Blockly.convertToGlobalUri() to convert local uri to global one

Source of the issue: blockly generates local uris even when tag is present which is incorrect (svg should react to tag), most browsers are 'smart enough' to convert local uris to global in that case (that browser behaviour is not part of specs though), but that doesn't happen in Safari
So I assume that isn't a safari bug and should be fixed in blockly

Solution was implemented and tested by me and works in Safari. Noticed no side effects in Chrome/Firefox/Edge/IE too.
Steps to reproduce:

  1. Take any blockly example
  2. Add tag to page
  3. Open the page in Safari
    Expected: white backround, correctly placed zoom/trash controls
    Actual: black backround, zoom/trash controls are visually multiplied (see screenshot at the beginning of this thread)

It would be great to see the code from Bohdan implemented. I experienced a similar issue after embedding Blockly into a React application. I narrowed down the root cause to a similar tag in my html document. Upon removing this tag, the black background in Safari was fixed.

Here's the better fix implementation in case somebody from the team wants to merge that and fix this issue:
https://github.com/angular/angular/issues/14843#issuecomment-427312579

JUST HOOK

Blockly.svgResize_raw = Blockly.svgResize;
Blockly.svgResize = function(obj) {
    Blockly.svgResize_raw(obj);
    if (obj.svgBackground_.style.fill.indexOf('#') != -1 && obj.svgBackground_.style.fill.indexOf('://') == -1) {
        var baseUri = window.location.hash ? window.location.href.replace(window.location.hash, "") : window.location.href;
        obj.svgBackground_.style.fill=obj.svgBackground_.style.fill.replace('#', baseUri+'#');
    }
};
// blocklyWorkspace.svgBackground_.style.fill=blocklyWorkspace.svgBackground_.style.fill.replace('#', 'http://127.0.0.1:9000/m/add-strategy#');
Blockly.utils.createSvgElement_raw = Blockly.utils.createSvgElement;
Blockly.utils.createSvgElement = function(name, attr, group) {
    var baseUri = window.location.hash ? window.location.href.replace(window.location.hash, "") : window.location.href;
    if (typeof(attr) !== 'undefined' && typeof(attr['clip-path']) == 'string') {
        var s = attr['clip-path'];
        if (s.indexOf('#') != -1 && s.indexOf('://') == -1) {
            var arr = s.split('#');
            arr[1] = baseUri + '#' + arr[1];
            attr['clip-path'] = arr.join('');
        }
    }
    return Blockly.utils.createSvgElement_raw(name, attr,group);
}

This looks like a life saver. But where exactly should I write this function? Not sure what you mean by Blockly Sources. :-/

Proper solution would be (finally):

  1. Add this function to Blockly sources
/**
 * Convert local uri to global uri, required for svg to work correctly when page contains <base> tag
 * Fixes Safari svg rendering issues (black background, misplaced zoom/trash controls etc)
 * @param {string} localUri local uri string
 * local uri example: "url(#blocklyTrashBodyClipPath837493)"
 * @return {!string} global uri
 */
Blockly.convertToGlobalUri = function (localUri) {
  var baseUrl = window.location.href.replace(window.location.hash, "");
  var idx = localUri.indexOf("url(");
  return localUri.slice(0, idx + 4) + baseUrl + localUri.slice(idx + 4);
};
  1. In all places where you see 'url(#' inside blockly sources, use Blockly.convertToGlobalUri() to convert local uri to global one

Source of the issue: blockly generates local uris even when tag is present which is incorrect (svg should react to tag), most browsers are 'smart enough' to convert local uris to global in that case (that browser behaviour is not part of specs though), but that doesn't happen in Safari
So I assume that isn't a safari bug and should be fixed in blockly

Solution was implemented and tested by me and works in Safari. Noticed no side effects in Chrome/Firefox/Edge/IE too.
Steps to reproduce:

  1. Take any blockly example
  2. Add tag to page
  3. Open the page in Safari
    Expected: white backround, correctly placed zoom/trash controls
    Actual: black backround, zoom/trash controls are visually multiplied (see screenshot at the beginning of this thread)

@samelhusseini do you have some time to check if this is still an issue for Angular on Safari?

This isn't an Angular specific thing. Our samples wouldn't repro because they don't use a router. In any framework that makes use of a router, the router would add a <base /> tag to figure out where it is.

You can repro something like this by adding <base href="/" /> in the playground.html and running a http-server. I'll look into this further to figure out what the right fix is.

According to https://stackoverflow.com/questions/18259032/using-base-tag-on-a-page-that-contains-svg-marker-elements-fails-to-render-marke/18265336#18265336 it seems the right fix is to include an absolute url when referencing the SVG filters, but I'm not sure how I feel about that as someone else could have the base point to some other URL and that would break things for them.

Maybe I'm missing something, but using absolute urls doesn't seem like something we can easily do generally in the Blockly library. Given that the base isn't necessarily where these resources are located would we need to add another API for a resource base url for these resources similar to the media path option?

Yeah I'm still learning about the tag to see what the fix should be, but my understanding so far is the tag basically says reference all relative paths using this new path instead of the document's URL path.
SVG urls like url(#blocklyZoomClipPath) that worked when they were referencing a relative id would no longer work since they now reference this new relative path based on the tag. More info on that here https://github.com/angular/angular.js/issues/8934

Couple of options, we could call into a utility method similar to what we do with Blockly.utils.xml.textToDomDocument making it easier for developers to return a url that works with the base tag.
Or we could add the absolute url ourselves. My concern there is I don't know how that would affect other developers not using the tag.

Hi, @samelhusseini, hope it's time to finally fix this in Blockly.

I've investigated this issue some time ago, and just refreshed my findings recently as I see some activity in the thread again. It seems Safari doesn't comply with to w3c specs for "(fill: url("#" - base should be combined with hash part (as other browsers do).

So let's just use absolute urls here.

https://html.spec.whatwg.org/multipage/semantics.html#the-base-element https://www.w3.org/2015/08/25-svg-minutes.html#item08.

There will be no side issues for developers which aren't using tag because absolute urls parsing isn't affected by base href.

@amish-thaker, here's a summary of my response to you, will put it here, in case anybody else needs it:

By blockly sources I mean the code here https://github.com/google/blockly.

I do not use the out-of -the-box compiled blockly library but build my own heavily modified version from those sources + my patches using google closure plovr.jar tool https://github.com/bolinfest/plovr.

In case you don't want to recompile, you can override appropriate methods after Blockly lib is loaded
Namely, add Blockly.convertToGlobalUri and override Blockly.WorkspaceSvg.prototype.createDom to call Blockly.convertToGlobalUri

Was this page helpful?
0 / 5 - 0 ratings

Related issues

ufalak picture ufalak  路  4Comments

rachel-fenichel picture rachel-fenichel  路  4Comments

samelhusseini picture samelhusseini  路  3Comments

rachel-fenichel picture rachel-fenichel  路  4Comments

GAUTAMRAJU15 picture GAUTAMRAJU15  路  5Comments